【问题标题】:EF6 DbSet<T> returns null in MoqEF6 DbSet<T> 在最小起订量中返回 null
【发布时间】:2014-10-23 12:44:16
【问题描述】:

我的应用程序中有一个典型的存储库模式设置,带有 DbContext (EF6):

public class MyDbContext : EFContext<MyDbContext> {

    public MyDbContext () { }

    public virtual DbSet<CartItem> Cart { get; set; }

还有一个存储库:

public class GenericEFRepository<TEntity, TContext>
    where TEntity : class, new()
    where TContext : EFContext<TContext> {

    private readonly TContext _context;

    public GenericEFRepository(TContext context) {
        _context = context;
    }
    //...
    public virtual TEntity Insert(TEntity item) {
        return _context.Set<TEntity>().Add(item);
    }

我正在使用 Moq 4.2(在 this tutorial 之后)通过创建一个模拟上下文对此进行测试:

        // Arrange
        var mockSet = new Mock<DbSet<CartItem>>();

        var mockContext = new Mock<MyDbContext>();
        mockContext.Setup(c => c.Cart).Returns(mockSet.Object);

        // Act
        var service = new GenericEFRepository<CartItem, MyDbContext>(mockContext.Object);
        service.Insert(new CartItem() {
            Id = 1,
            Date = DateTime.Now,
            UserId = 1,
            Detail = string.Empty
        });

        // Assert
        mockSet.Verify(s => s.Add(It.IsAny<CartItem>()), Times.Once());

问题是当我到达这一行时:

return _context.Set<TEntity>().Add(item);

_context.Set&lt;TEntity&gt;() 返回空值。经过一番谷歌搜索后,在 EF5 中似乎有必要返回 IDbSet&lt;T&gt; 以便 Moq 模拟该集合,但在 EF6 中则不需要。是不是这样,还是我错过了什么?

【问题讨论】:

  • 我是否错过了您设置Set&lt;T&gt;() 方法以返回mockSet 的位置?
  • 您必须澄清这一点。根据我链接到的教程,与集合的唯一交易是 var mockSet = new Mock&lt;DbSet&lt;CartItem&gt;&gt;();mockContext.Setup(c =&gt; c.Cart).Returns(mockSet.Object); 行。
  • 没错,但是在教程的服务代码中(快速浏览后)我没有看到他们调用Set&lt;T&gt;(),而你的却是。为此添加一个设置,我认为它会起作用。
  • 看来你是对的!我想我假设因为Cart.Set&lt;CartItem&gt;() 指的是同一件事,所以设置Cart 就足够了。谢谢!

标签: c# entity-framework unit-testing moq entity-framework-6


【解决方案1】:

在 2020 年底使用 EntitiFramework Core 时,这个解决方案对我来说仍然是正确的。 不太容易理解如何模拟对象/数据集,我现在开始使用内存数据库和模拟数据来实现一些集成测试。 我看到我的方法正常工作,例如,如果我这样做:

await _dbcontext.MyEntirySet.ToListAsync();

但在通用存储库中使用 equivalend 时失败

_dbcontext.Set<TEntity>  : this return a null dataset.

即使使用 EntityFramework Core,我也可以确认 mocking Set 可以解决问题。

 _dbContextMock.Setup(c => c.Set<MyEntityType>()).Returns(mock.Object);

【讨论】:

    【解决方案2】:

    Set&lt;T&gt;() 方法添加设置:

    mockContext.Setup(c => c.Set<CartItem>()).Returns(mockSet.Object);
    

    即使在真实的EFContext 上,属性CartSet&lt;CartItem&gt;() 指的是同一个对象,但上下文的mock 并不知道这一点,所以你需要告诉它明确返回什么。

    由于它是一个松散的模拟,对尚未设置的方法的调用返回默认值,在本例中为null。严格的模拟可以很好地帮助找到这个错误,而且have maintenance costs that other folks don't want to deal with

    【讨论】:

    • 还可能需要模拟 DbSet 上的其他函数。
    • @Patrick 这对我不起作用。 EF6 和代码大体相同。但是我对通过存储库访问我的上下文的服务进行了测试。那么解决办法是什么?
    • 您会感到惊讶,但我没有使用 DbSet 设置 mockContext 并且一切正常。但在代码中,我需要使用属性名称,如示例 - 属性购物车。如果我与 Set 一起使用,则它无法初始化 DbSet。
    猜你喜欢
    • 1970-01-01
    • 2021-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多