在 Asp.net Core:Invalid 非虚拟设备上使用最小起订量问题(在 VB 中可覆盖)
Issue using Moq in Asp.net Core:Invalid setup on a non-virtual (overridable in VB)
我在 asp.net 核心中使用 xunit 和 Moq 编写了一个单元测试,为此我遵循了这个 Article,但是我得到了这个错误:
Invalid setup on a non-virtual (overridable in VB) member: 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());
}
这些是我的模型class:
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,但它给出了另一个错误:
Can not instantiate proxy of class: EntitFrameworkCore.Models.Context
有什么问题?
确保 Context
class 中的 Blogs
属性 可覆盖
public class Context : DbContext
{
public virtual DbSet<Blog> Blogs { get; set; }
}
您还应该考虑抽象化数据库上下文。
public interface IContext {
DbSet<Blog> Blogs { get; set; }
}
public class Context : DbContext, IContext {
public virtual DbSet<Blog> Blogs { get; set; }
}
现在,如果 Context 属性是虚拟的,甚至都没有关系,因为您将在单元测试时模拟继承的接口。
您的 classes 也应该只依赖于抽象而不是实现细节。
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());
}
错误信息已经很清楚了。
mockContext.Setup(m => m.Blogs)
.Returns(mockDbSet.Object);
我假设 Blogs
是您的 DbSet<Blog>
属性,它不是虚拟的。模拟仅适用于标记为 virtual
的 method/properties 或接口。
您真的不必嘲笑您的 DbContext
。而是使用由内存数据库支持的 DbContext
。请参阅 docs 了解如何设置上下文以使用内存提供程序,这是 InMemory
提供程序的主要目的。
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "sompe_database_name")
.Options;
var context = new AppDbContext(options);
我在 asp.net 核心中使用 xunit 和 Moq 编写了一个单元测试,为此我遵循了这个 Article,但是我得到了这个错误:
Invalid setup on a non-virtual (overridable in VB) member: 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());
}
这些是我的模型class:
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,但它给出了另一个错误:
Can not instantiate proxy of class: EntitFrameworkCore.Models.Context
有什么问题?
确保 Context
class 中的 Blogs
属性 可覆盖
public class Context : DbContext
{
public virtual DbSet<Blog> Blogs { get; set; }
}
您还应该考虑抽象化数据库上下文。
public interface IContext {
DbSet<Blog> Blogs { get; set; }
}
public class Context : DbContext, IContext {
public virtual DbSet<Blog> Blogs { get; set; }
}
现在,如果 Context 属性是虚拟的,甚至都没有关系,因为您将在单元测试时模拟继承的接口。
您的 classes 也应该只依赖于抽象而不是实现细节。
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());
}
错误信息已经很清楚了。
mockContext.Setup(m => m.Blogs)
.Returns(mockDbSet.Object);
我假设 Blogs
是您的 DbSet<Blog>
属性,它不是虚拟的。模拟仅适用于标记为 virtual
的 method/properties 或接口。
您真的不必嘲笑您的 DbContext
。而是使用由内存数据库支持的 DbContext
。请参阅 docs 了解如何设置上下文以使用内存提供程序,这是 InMemory
提供程序的主要目的。
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "sompe_database_name")
.Options;
var context = new AppDbContext(options);