模拟 Func<> 的适当方式

Appropriate way to mock Func<>

嗨,我正在尝试模拟以下内容:

var result = _scope.Execute<FooService, IList<FooEntity>>(x => x.GetFooEntities(fooModel));

这就是我尝试模拟它的方式:

_mockedScope
    .Setup(x => x.Execute<FooService, IList<FooEntity>>(f => f.GetFooEntities(It.IsAny<FooModel>())))
    .Returns(new List<FooEntity>)

但是当我 运行 测试时它抛出一个异常

Unsupported expression: s => s.GetFooEntities(IsAny())

有什么建议可以模拟吗?

这是我想要最小起订量的例子

public class Test
{
    private readonly IScope _scope;

    public Test(IScope scope)
    {
        _scope = scope;
    }

    public void Foo()
    {
        var foo = new FooEntity();

        Result<IList<Foo>> result =
            _scope.Execute<FooService, IList<Foo>>(
                "f",
                s => s.GetFoo(foo));
    }
}

public class Foo
{
}

public class FooEntity
{
}

public class FooService
{
    public List<Foo> GetFoo(FooEntity f);
}

public interface IScope
{
    Result<TResult> Execute<T1, TResult>(string temp, Func<T1, TResult> function);
}

public class Result<T>
{
    private Result(T value, Exception exception)
    {
        Value = value;
        Error = exception;
    }

    public Exception Error { get; }

    public T Value { get; }

    public bool HasError => Error != null;

    public static Result<T> Fail(Exception exception) => new Result<T>(default(T), exception);

    public static Result<T> Success(T value) => new Result<T>(value, null);
}

虽然最小起订量使用表达式来设置模拟,但您正在尝试模拟表达式。这对于最小起订量来说往往非常困难,但通过匹配的参数有解决方法。

假设 Scope.Execute 方法定义为

public interface IScope {
    Result<TResult> Execute<T, TResult>(string temp, Func<T, TResult> function);
}

在设置依赖于表达式参数的模拟时,使用 It.IsAny 允许灵活性。

_mockedScope
    .Setup(x => x.Execute<FooService, IList<Foo>>(It.IsAny<string>(), It.IsAny<Func<FooService, IList<Foo>>>()))
    .Returns(Result<IList<Foo>>.Success(new List<Foo>()));

It.IsAny<Func<FooService, IList<Foo>>>() 将覆盖调用代码中的 s => s.GetFoo(foo)

给定

public class Test {
    private readonly IScope _scope;

    public Test(IScope scope) {
        _scope = scope;
    }

    public IList<Foo> Foo() {
        var foo = new FooEntity();

        Result<IList<Foo>> result = _scope.Execute<FooService, IList<Foo>>("f", s => s.GetFoo(foo));

        var value = result.Value;

        return value;
    }
}

下面的完整示例用于演示上面解释的内容

[TestClass]
public class ExpressionMock {
    [TestMethod]
    public void TestFoo() {
        //Arrange
        var _mockedScope = new Mock<IScope>();

        _mockedScope
            .Setup(x => x.Execute<FooService, IList<Foo>>(It.IsAny<string>(), It.IsAny<Func<FooService, IList<Foo>>>()))
            .Returns(Result<IList<Foo>>.Success(new List<Foo>()));

        var subject = new Test(_mockedScope.Object);

        //Act
        var actual = subject.Foo();

        //Assert
        actual.Should().NotBeNull();
    }
}

参考Moq Quickstart以更好地了解如何使用该框架