我在asp.net核心中使用xunit和Moq编写单元测试,为此我遵循此Article,但我收到此错误:
非虚拟(VB中可覆盖)成员的设置无效:m => m.Blogs
这是我试过的:
[Fact]
public void CreateBlog()
{
var mockDbSet = new Mock<DbSet<Blog>>();
var mockContext = new Mock<Context>();
mockContext.Setup(m => m.Blogs).Returns(mockDbSet.Object); //In this line i got error
var service = new BlogController(mockContext.Object);
service.AddBlog("ADO.NET Blog", "adtn.com");
mockDbSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
这些是我的模特课:
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public virtual IList<Post> Posts { get; set; }
}
和
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime RegisterDate { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
这是我的控制者:
public class BlogController : Controller
{
private readonly Context _context;
public BlogController(Context ctx)
{
_context = ctx;
}
[HttpPost]
public Blog AddBlog(string name, string url)
{
var blog = new Blog() { Name = name, Url = url };
_context.Blogs.Add(blog);
_context.SaveChanges();
return blog;
}
}
更新:我在上下文虚拟中创建dbset,但它给出了另一个错误:
无法实例化类的代理:EntitFrameworkCore.Models.Context
问题是什么?
答案 0 :(得分:1)
确保Blogs
类中的Context
属性可以覆盖
public class Context : DbContext
{
public virtual DbSet<Blog> Blogs { get; set; }
}
您还应该考虑抽象db上下文。
public interface IContext {
DbSet<Blog> Blogs { get; set; }
}
public class Context : DbContext, IContext {
public virtual DbSet<Blog> Blogs { get; set; }
}
现在,如果Context属性是虚拟的,则无关紧要,因为在单元测试时您将模拟继承的接口。
您的课程也应仅依赖于抽象而非实施细节。
public class BlogController : Controller {
private readonly IContext context;
public BlogController(IContext context) {
this.context = context;
}
//...other code
}
这样可以在隔离单元测试期间安全地进行模拟。
[Fact]
public void CreateBlog() {
//Arrange
var mockDbSet = new Mock<DbSet<Blog>>();
var mockContext = new Mock<IContext>();
mockContext.Setup(m => m.Blogs).Returns(mockDbSet.Object);
var service = new BlogController(mockContext.Object);
//Act
service.AddBlog("ADO.NET Blog", "adtn.com");
//Assert
mockDbSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
答案 1 :(得分:1)
错误信息已经非常明确了。
mockContext.Setup(m => m.Blogs)
.Returns(mockDbSet.Object);
我认为Blogs
是您的DbSet<Blog>
属性,它不是虚拟的。模拟仅适用于标记为virtual
的方法/属性或接口。
你真的不应该嘲笑你的DbContext
。而是使用内存数据库支持的DbContext
。有关如何设置上下文以使用内存提供程序的信息,请参阅docs,这是InMemory
提供程序的主要目的。
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "sompe_database_name")
.Options;
var context = new AppDbContext(options);