带有委托/lambda 参数的存根方法调用

Stub methodcalls with delegate / lambda parameters

我有一个界面 class 看起来像这样:

public interface IServiceFacade
{
     TResult Execute<TResult>(Func<IService, TResult> operation);
}

public class ServiceFacade : IServiceFacade
{
    private readonly string endpoint = "EndPoint";

    public TResult Execute<TResult>(Func<IService, TResult> operation)
    {
         // Call to remote WCF service that results in a TResult

         return TResult;
    }
}

IService 接口表示远程 WCF 服务 运行,因此在 class.

中没有可用的该接口的实现实例

然后我像这样调用这个方法两次:

public class ServiceConsumer
{
     public ServiceConsumer(IServiceFacade serviceFacade)
     {
         var returnInteger1 = serviceFacade.Execute(service => service.Method1("StringArgument1"));
         var returnInteger2 = serviceFacade.Execute(service => service.Method1("StringArgument2"));
     }
}

在我的单元测试中,我想将第一次调用的返回值存根为 1,第二次调用为 2。

示例测试方法

[Test]
public void TestMethod()
{
    var serviceFacadeStub = MockRepository.GenerateStub<IServiceFacade>();
    serviceFacadeStub.Stub(call => call.Execute(Arg<Func<IService, int>.Matches(?))).Return(1);     
    serviceFacadeStub.Stub(call => call.Execute(Arg<Func<IService, int>.Matches(?))).Return(2);

    var sut = new ServiceConsumer(serviceFacadeStub);    

}

我不知道要在 Matches 中放什么,或者我最好还是使用火柴以外的东西。

我现在正在使用 RhinoMocks 和 NUnit,但如果有更好的框架来做到这一点,我愿意接受建议。

在大多数情况下,使用 Func/Action 模拟方法有点棘手(在我知道的任何模拟框架中...)

通常你必须执行给定的 Func/Action 然后结果应该会影响被测单元的其余部分。

以下代码段显示了解决问题的最简单方法:

[Test]
public void TestMethod(
{

    var fakeService = MockRepository.GenerateStub<IService>();
    fakeService.Stub(x => x.Method1("StringArgument1")).Return(1);
    fakeService.Stub(x => x.Method1("StringArgument2")).Return(2);

    var serviceFacadeStub = MockRepository.GenerateStub<IServiceFacade>();
    serviceFacadeStub.Stub(call => call.Execute(Arg<Func<IService, int>>.Is.Anything))
            .WhenCalled(invocation =>
            {
                var func = (Func<IService, int>)invocation.Arguments[0];
                invocation.ReturnValue = func(fakeService);
            }).Return(0);

    var shouldBeOne = serviceFacadeStub.Execute(service => service.Method1("StringArgument1"));
    var shouldBeTwo = serviceFacadeStub.Execute(service => service.Method1("StringArgument2"));

    Assert.AreEqual(1, shouldBeOne);
    Assert.AreEqual(2, shouldBeTwo);

}

我用 fakeService 执行 lambda,然后 return 基于给定参数的结果。 另一种选择是像你一样使用 Matches ,但是你必须使用反射来分析 lambda...(它要复杂得多....)

万一其他人遇到同样的问题,我会 post 我想出了什么。我开始改用 NSubstitute,这很简单

var serviceSub = Substitute.For<IService>();
serviceSub.Method1(Arg.Is<string>(arg => arg == "StringArgument1")).Returns(1);
serviceSub.Method1(Arg.Is<string>(arg => arg == "StringArgument2")).Returns(2);

var mobileServiceFacadeSub = Substitute.For<IServiceFacade>();
mobileServiceFacadeSub.Execute(Arg.InvokeDelegate<Func<IService, ICollection<TResult>>>(serviceSub));