【问题标题】:DbSet mock, no results while calling ToList secondlyDbSet mock,第二次调用 ToList 时没有结果
【发布时间】:2014-05-04 19:54:44
【问题描述】:

我正在尝试模拟 DbContext 和 DbSet。这适用于我之前的单元测试,但是当我的代码第二次在 DbSet 上调用 ToList 方法时出现问题。

第一个 dbSet.ToList() 返回模拟结果。 第二个返回0个元素;

       var queryableData = new List<string>{ "a", "b", "c" }.AsQueryable();

        var mockDbSet = new Mock<DbSet<string>>();
        var q = mockDbSet.As<IQueryable<string>>();
        q.Setup(m => m.Provider).Returns(queryableData.Provider);
        q.Setup(m => m.Expression).Returns(queryableData.Expression);
        q.Setup(m => m.ElementType).Returns(queryableData.ElementType);
        q.Setup(m => m.GetEnumerator()).Returns(queryableData.GetEnumerator());

        DbSet<string> dbset = mockDbSet.Object;
        IQueryable<string> query = dbset;

        //RESULTS: abc
        var a1 = dbset.ToList();
        foreach (var a in a1)
            Console.Write(a);

        //NO RESULTS
        var a2 = dbset.ToList();
        foreach (var a in a2)
            Console.Write(a);

【问题讨论】:

    标签: c# .net entity-framework unit-testing ado.net


    【解决方案1】:

    每次调用GetEnumerator 时,您都会返回相同的枚举数实例。当它枚举一次时,它就完成了,EF 不会调用它的Reset 方法,而是要求一个新的枚举器。

    但是你返回一个刚刚产生了所有元素并且不再产生的元素。

    相反,返回一个返回枚举器的函数,它会在您每次请求时返回一个新的枚举器。

     q.Setup(m => m.GetEnumerator()).Returns( () => queryableData.GetEnumerator() );
    

    【讨论】:

    • 这可要了我的命。很棒的收获。
    • 这几个月来对我来说一直是个熊。我终于陷入了无法避免的境地。这帮助很大!
    • 这让我的生活变得更好了。谢谢。
    • OOP 的圣母,你成就了我的一天!
    【解决方案2】:

    我只是想在 Wiktor Zychla 的回答中添加我的一小部分。如果有人正在寻找这个模拟的异步版本(来自本教程:http://msdn.microsoft.com/en-us/data/dn314429.aspx#async),那么这是我对TestDbAsyncEnumerator&lt;T&gt; 类的修改:

    internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>
    {
        private readonly IEnumerator<T> _inner;
    
        public TestDbAsyncEnumerator(IEnumerator<T> inner)
        {
            _inner = inner;
        }
    
        public TestDbAsyncEnumerator(Func<IEnumerator<T>> valueFunction)
        {
            _inner = valueFunction();
        }
    
        public void Dispose()
        {
            _inner.Dispose();
        }
    
        public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
        {
            return Task.FromResult(_inner.MoveNext());
        }
    
        public T Current
        {
            get { return _inner.Current; }
        }
    
        object IDbAsyncEnumerator.Current
        {
            get { return Current; }
        }
    }
    

    然后就像 Wiktor 建议的那样,您必须使用委托设置它,所以在异步的情况下,它会是这样的:

    mockSet.As<IDbAsyncEnumerable<Blog>>()
                .Setup(m => m.GetAsyncEnumerator())
                .Returns(new TestDbAsyncEnumerator<Blog>(() => data.GetEnumerator()));
    

    如果有人想要它的来源,那么你去:https://github.com/kelostrada/EntityFrameworkWithMock.Test

    【讨论】:

    • 您的 GitHub 存储库中的代码与您在 stackoverflow 中的代码不匹配?在我的情况下都不起作用。
    • 什么不适合你?我已经有 2 年多没有看过这个代码了,它可能因为一些包更新和类似的东西而无法工作。它不编译吗?除此之外,我发现尝试模拟 DbSet 就像我曾经做过的最艰巨的任务……再也不会尝试那样做。只需为测试设置一个新数据库并为每个测试设置一个事务 - 更可靠且易于使用的解决方案。 (也许只是慢一点)
    • 我的评论更多是关于这里的代码,而您链接的存储库中的代码不匹配。您的代码已构建,但未解决此处描述的类似问题:stackoverflow.com/questions/41899177/…。现在了解模拟 DbSets 是一个非常困难的方法,也许不是最好的,认为 EF 是可测试的,但也许不是。现在改变项目工作中的事情为时已晚。谢谢。
    【解决方案3】:

    由于我没有足够的声誉,我无法对 Wiktor 的帖子发表评论,但我想在 Wiktor 的回答中补充一点。

    代码

    mockSet.Setup((m => m.GetEnumerator()).Returns(() => data.GetEnumerator())
    

    如果你像例子中那样给它一个零参数 lambda 将失败(至少我正在使用的版本)。 但是,传递从未使用过的参数将使其满足签名要求。

    mockSet.Setup((m => m.GetEnumerator()).Returns(x => data.GetEnumerator())
    

    但是会编译,并按预期工作。

    这也适用于 Kelu 的回答(尽管他在错误的地方添加了 lambda。

    mockSet.As<IDbAsyncEnumerable<Blog>>()
            .Setup(m => m.GetAsyncEnumerator())
            .Returns(x => new TestDbAsyncEnumerator<Blog>(data.GetEnumerator()));
    

    我不想在这里挑剔答案,我只是补充一下,因为我自己犯了这些错误:)

    【讨论】:

      【解决方案4】:

      如果您在 .ToList() 调用之前放置“Where”子句,则数据应保持存在。

      var a1 = dbset.Where(m => m != null).ToList();
      
      var a2 = dbset.Where(m => m != null).ToList();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多