c# - 如何在 Moq 中模拟链式 Linq 方法

标签 c# linq unit-testing mocking moq

我有一个查询

var myRecord = myRepository.All().Any(e => e.Id == id);

我用这个来 mock 它

this._myMockRepository
    .Setup(s => s.All().Any(It.IsAny<Expression<Func<MyObject, bool>>>()))
    .Returns(this.GetFakeMyObjects().Any(t => t.Id == 1));

如何模拟 .All().Any(...) 调用?我得到的异常是 NotSupportedException 消息:

Expression references a method that does not belong to the mocked object: s => s.All().Any < MyRepositoryObject > (It.IsAny < Expression`1 > ())

我试过这样分解它

this._myMockRepository
    .Setup(s => s.All(It.IsAny<Expression<Func<MyObject, bool>>>()))
    .Returns(this.GetFakeMyObjects());
this._myMockRepository
    .Setup(s => s.All().Any(It.IsAny<Expression<Func<MyObject, bool>>>()))
    .Returns(this.GetFakeMyObjects().Any(t => t.Id == 1));

对于 Moq 来说还很新,所以任何提示都将不胜感激。

最佳答案

更新

如果 All() 不是扩展方法,则更新模拟设置以在调用时返回可查询。

this._myMockRepository
    .Setup(_ => _.All())
    .Returns(this.GetFakeMyObjects());//<-- assuming GetFakeMyObjects returns an enumerable

这假设一个存储库定义了类似的东西

public interface IRepository {
    IEnumerable<MyObject> All();
}

或接近该定义。


原始答案

这些都是扩展方法。 Moq 不模拟扩展方法。

您需要模拟的是调用模拟成员时返回的满足提供的 linq 查询的内容。

基于代码片段的假设表明模拟的主题是可枚举的。

以下扩展方法可用于模拟在可枚举模拟上执行查询所需的行为。

/// <summary>
/// Converts a generic <seealso cref="System.Collections.Generic.IEnumerable&lt;T&gt;"/>  to a <see cref="Moq.Mock"/> implementation of queryable list 
/// </summary>
public static Mock<T> SetupQueryable<T, TItem>(this Mock<T> queryableMock, IEnumerable<TItem> source)
    where T : class, IEnumerable<TItem> {

    var queryableList = source.AsQueryable();

    queryableMock.As<IQueryable<TItem>>().Setup(x => x.Provider).Returns(queryableList.Provider);
    queryableMock.As<IQueryable<TItem>>().Setup(x => x.Expression).Returns(queryableList.Expression);
    queryableMock.As<IQueryable<TItem>>().Setup(x => x.ElementType).Returns(queryableList.ElementType);
    queryableMock.As<IQueryable<TItem>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator());

    return queryableMock;
}

并且可以像这样使用。

var fakeItems = GetFakeMyObjects();
var _myMockRepository = new Mock<IMyRepository>()
_myMockRepository.SetupQueryable(fakeItems);

这将允许在测试时在模拟对象上调用查询。

var myRepository = _myMockRepository.Object;
var myRecord = myRepository.All().Any(e => e.Id == id);

关于c# - 如何在 Moq 中模拟链式 Linq 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43982574/

相关文章:

c# - 仅当 LINQ 中有条件时才选择

c# - 在 Entity Framework 中联合或连接自定义数据

c# - 具有多对多关系的linq查询

php - 如何在 PHPUnit 测试中覆盖 Laravel 的异常处理程序?

c# - 枚举指向游戏对象?

c# - 递归地将树结构添加到列表 C#

c# - C# 中巧妙的 5x5 字母网格

ruby-on-rails - 测试method_defined? ActiveRecord 类在实例化之前不起作用

c++ - cppUnit 在启动每个单元测试时是否使用 fork()?

c# - 删除列表中项目的最快方法