首页 > 解决方案 > 起订量设置报告“表达式不是方法调用”

问题描述

我计划为我们拥有的一堆服务的代理/包装器编写一个测试库。每个代理都简单地将模型转换为 TIn,调用返回 TOut 的服务上的方法。对于每个代理,唯一的变化是底层服务与每个代理内部的样板代码不同。

class TestBase<TSvc, TProxy>
{
    private Mock<TSvc> svcMock = new Mock<TSvc>();
    private Mock<IMapper> mapperMock = new Mock<IMapper>();
    private TProxy proxy;

    protected void TestServiceCall<TIn, TOut>(Func<TSvc, Func<TIn, TOut>> svcCallFunc, TInModel model,
    Func<TProxy, TInModel, TOutModel> methodToTest)
    {
        var input= new TIn();
        var svcResult = new TOut();
        var proxy = ConstructProxy(this.svcMock);

        this.mapperMock.Setup(m => m.Map<TInModel, TInput>(model)).Returns(input);
        this.svcMock.Setup(m => svcCallFunc(m)(input)).Returns(svcResult);
        this.mapperMock.Setup(m => m.Map<TOut, TOutModel>(svcResult)).Returns(output);

        var result = methodToTest(model);

        ... verify if the svc was called etc. ...

        result.Should().BeSameAs(output);
    }
} 

class UserService
{
    QuestionModels GetQuestionsByUsers(SearchModel searchModel)
    { .. get all questions that comply with search parameters..}

    TopicModels GetTopicsForUsers(TopicSearchModel searchModel)
    { .. get all topics that comply with search parameters..}
}

class UserProxy
{
    private UserService service;
    private Mapper mapper;

    QuestionsClass GetQuestionsByUsers(SearchClass search)
    {
        var searchModel = mapper.Map<SearchClass, SearchModel>(search);

        var svcResult = userService.GetQuestionsByUsers(searchModel);

        return mapper.Map<QuestionModels, QuestionsClass>(svcResult);
    }

    TopicsClass GetTopicsForUsers(TopicSearchClass search)
    {
        var searchModel = mapper.Map<TopicSearchClass, TopicSearchModel>(search);

        var svcResult = userService.GetTopicsForUsers(searchModel);

        return mapper.Map<TopicModels, TopicsClass>(svcResult);
    }
}

如果您可以看到,每个方法需要执行的步骤数才能达到结果是恒定的,

  1. 将请求映射到中间类型
  2. 致电服务以获取结果
  3. 映射&返回结果类

我有几十个这样的类,它们的作用几乎相同。他们每个人都可以有大约 5-10 种这样的方法。

所以我想做以下事情。这样我就可以节省编写样板测试代码所花费的时间。

[TestClass]
class UserProxyTest : TestBase<UserService, UserProxy>
{
    [TestMethod]
    void GetQuestionsByUsersTest()
    {
        this.TestServiceCall<SearchModel, QuestionModels>(
        svc => svc.GetQuestionsByUsers,
        new SearchClass(), <-- This could be anything 
        pxy => pxy.GetQuestionsByUsers);
    }

    [TestMethod]
    void GetTopicsForUsers()
    {
        this.TestServiceCall<TopicSearchModel, TopicModels>(
        svc => svc.GetTopicsForUsers,
        new TopicSearchClass(), <-- This could be anything 
        pxy => pxy.GetTopicsForUsers);
    }   
}

我遇到的问题是,设置 svcMock 的行报告了一个错误,指出表达式不是方法调用。我该如何解决这个问题?

标签: c#moq

解决方案


svcMock.Setup(m => svcCallFunc(m)(input))是无效的。

onSetup方法Mock<T>接受一个Expression用于选择T要模拟的成员。表达式的主体必须是成员访问表达式或对成员的方法调用表达式T。其他任意表达式无效。

例如:

public interface IService1
{
    int Test { get; }
    void Method1();
    object Method2(object input);
}

var mock = new Mock<IService1>();

// These are valid member selectors
mock.Setup(x => x.Test);
mock.Setup(x => x.Method1());
mock.Setup(x => x.Method2(null));

// These are not
mock.Setup(x => 1);
mock.Setup(x => x.Test.GetHashCode());
mock.Setup(x => this);

要对 进行有效调用Setup,您需要 的实例Expression<Func<TSvc, TOut>>或 的实例Expression<Action<TSvc>>,因此您可以将TestServiceCall方法更改为:

protected void TestServiceCall<TIn, TOut>(
    Func<TIn, Expression<Func<TSvc, TOut>>> svcCallFunc)
{
    var input = new TIn();
    var output = new TOut();

    svcMock.Setup(svcCallFunc(input)).Returns(output);
}

并称之为:

TestServiceCall<object, object>(input => svc => svc.Method2(input));

推荐阅读