【问题标题】:Issue using Moq in Asp.net Core:Invalid setup on a non-virtual (overridable in VB)在 Asp.net Core 中使用 Moq 的问题:非虚拟机上的无效设置(在 VB 中可覆盖)
【发布时间】:2017-05-12 20:12:41
【问题描述】:

我在 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;
    }
}

更新:我在上下文中创建了虚拟数据库,但它给出了另一个错误:

无法实例化类的代理:EntitFrameworkCore.Models.Context

有什么问题?

【问题讨论】:

  • 错误信息说 ;)
  • 我按文章说的做,所以文章出错了?
  • 上下文中的博客属性需要是虚拟的
  • 你也可以抽象上下文。因为模拟实际上是在创建一个仍在尝试连接到数据库的上下文实例
  • Mock 无法实例化它,因为 DbContext 没有无参数构造函数。在下面查看我的答案并考虑使用 InMemoryDb 或使用 DbContextOptionsBuilder 自己构建它,但是你不能模拟它并且必须使用在单元测试中不行的真实数据库

标签: c# unit-testing asp.net-core moq


【解决方案1】:

确保Context 类中的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 属性是虚拟的也没关系,因为您将在单元测试时模拟继承的接口。

你的类也应该只依赖于抽象而不是实现细节。

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());
}

【讨论】:

  • 它给了我另一个错误:Can not instantiate proxy of class: EntitFrameworkCore.Models.Context你知道吗?
  • 从未尝试在接口中拆分上下文(我更喜欢其他形式的抽象),但是 ASP.NET Core IoC 是否能够在 Startup. cs ?
  • @Tseng 你更喜欢其他形式的抽象!你是什​​么意思?
  • @pejman:存储库,CQRS。但两者都超出了问题的范围
  • @Tseng 您需要在启动时使用 IoC 容器对其进行配置。 services.AddScoped&lt;IContext&gt;(p=&gt; new Context(options))
【解决方案2】:

错误信息已经很清楚了。

mockContext.Setup(m => m.Blogs)
    .Returns(mockDbSet.Object);

我假设Blogs 是您的DbSet&lt;Blog&gt; 属性,它不是虚拟的。模拟仅适用于标记为 virtual 的方法/属性或接口。

你真的不应该嘲笑你的DbContext。而是使用由内存数据库支持的DbContext。请参阅docs,了解如何设置上下文以使用内存提供程序,这是InMemory 提供程序的主要目的。

var options = new DbContextOptionsBuilder<BloggingContext>()
    .UseInMemoryDatabase(databaseName: "sompe_database_name")
    .Options;

var context = new AppDbContext(options);

【讨论】:

  • 最好在内存数据库中使用(并有效地将其转化为集成测试)。您的代码没有很好地分离/抽象并且与持久层紧密耦合,并且您的数据库问题泄漏到应用程序的其他部分。这就是你遇到麻烦的原因
  • 我读过一些使用接口作为上下文的文章,我喜欢它们很好吗?
  • 这可能是一个有效的选项(参见 Nikosi 的示例),尽管您仍然可能会遇到 IQueryable&lt;T&gt;IDbSet&lt;T&gt; 泄漏到您的图层的问题,因为这允许在任何部分修改查询应用程序(这可能会导致一些意外的连接和意外的低性能)。对于学习或小型应用程序来说很好,但在企业级应用程序中可能是一个问题
猜你喜欢
  • 2015-09-05
  • 1970-01-01
  • 2014-03-13
  • 1970-01-01
  • 2017-03-29
  • 1970-01-01
  • 2014-07-19
  • 1970-01-01
相关资源
最近更新 更多