C# 在Asp.net Core中使用Moq的问题:在非虚拟服务器上的设置无效(在VB中可重写)
我在asp.net core中使用xunit和Moq编写了一个单元测试,为此我遵循了以下步骤,但出现了以下错误: 非虚拟(在VB中可重写)成员上的设置无效:m=>m.Blogs 这就是我所尝试的:C# 在Asp.net Core中使用Moq的问题:在非虚拟服务器上的设置无效(在VB中可重写),c#,unit-testing,asp.net-core,moq,C#,Unit Testing,Asp.net Core,Moq,我在asp.net core中使用xunit和Moq编写了一个单元测试,为此我遵循了以下步骤,但出现了以下错误: 非虚拟(在VB中可重写)成员上的设置无效:m=>m.Blogs 这就是我所尝试的: [Fact] public void CreateBlog() { var mockDbSet = new Mock<DbSet<Blog>>(); var mockContext = new Mock<Context&g
[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 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的代理
问题出在哪里?确保
上下文
类中的博客
属性是可重写的
public class Context : DbContext
{
public virtual DbSet<Blog> Blogs { get; set; }
}
以便在隔离单元测试期间安全模拟
[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());
}
[事实]
public void CreateBlog(){
//安排
var mockDbSet=new Mock();
var mockContext=new Mock();
Setup(m=>m.Blogs).Returns(mockDbSet.Object);
var service=newblogcontroller(mockContext.Object);
//表演
AddBlog(“ADO.NET博客”、“adtn.com”);
//断言
mockDbSet.Verify(m=>m.Add(It.IsAny()),Times.Once());
验证(m=>m.SaveChanges(),Times.Once());
}
错误信息已经非常清楚了
mockContext.Setup(m => m.Blogs)
.Returns(mockDbSet.Object);
我假设Blogs
是您的DbSet
属性,它不是虚拟的。mock只在标记为virtual
的方法/属性或接口上工作
你不应该真的去模仿你的DbContext
。而是使用由内存数据库支持的DbContext
。请参见如何设置上下文以使用inmemory提供程序,这是inmemory提供程序的主要用途
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "sompe_database_name")
.Options;
var context = new AppDbContext(options);
var options=new DbContextOptionsBuilder()
.UseInMemoryDatabase(数据库名称:“sompe\u数据库名称”)
.选择;
var context=新的AppDbContext(选项);
错误消息会显示它;)我照文章说的做,所以文章出错了?上下文中的Blogs属性需要是虚拟的,你也可以抽象上下文。因为mock实际上正在创建一个仍在尝试连接到数据库的上下文实例,mock无法实例化它,因为DbContext没有无参数构造函数。请看下面的答案,并考虑使用iMouthyDB或使用dBraveTopStudioBu建器自己构建,但是你不能模仿它,必须使用真正的DB,这是不可进入的单元测试。这给了我另一个错误:不能实例化类的代理:EntitFrameworkCore.Models.Context
你对此有什么想法吗?请不要尝试在接口中拆分上下文(我更喜欢其他形式的抽象),但是ASP.NET核心IoC是否能够在不使用显式的services.AddScpüed(provider=>provider.GetService())的情况下注入它代码>调用Startup.cs?@Tseng u更喜欢其他形式的抽象!你的意思是什么?@pejman:Repository,CQRS。但这两项都超出了本报告的范围question@Tseng您需要在启动时使用IoC容器对其进行配置services.AddScoped(p=>newcontext(options))
最好使用内存数据库(并有效地将其转换为集成测试)。您的代码没有很好地分离/抽象,并且与持久性层紧密耦合,您的数据库关注点泄漏到应用程序的其他部分。这就是为什么你在itI上有问题的原因。我读了一些文章,其中使用了一个接口作为他们的上下文,我很喜欢他们,这是好事?这可能是一个有效的选择(见Nikosi的例子),尽管您仍然可能会遇到IQueryable
和IDbSet
泄漏到您的层中的问题,因为这允许在应用程序的任何部分修改查询(这可能会导致一些意外的连接和意外的低性能)。对于学习型应用程序或小型应用程序来说,这很好,但在企业级应用程序中可能会出现问题
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);
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "sompe_database_name")
.Options;
var context = new AppDbContext(options);