使用 NSubstitute 模拟动作 <T>

Mocking Action<T> with NSubstitute

Web 服务的代理需要单元测试 - 显然 - 不影响 Web 服务。

这是我想要绝育的方法 -

public void Invoke(Action<T> action)
{
    Task.Run(async ()=> { await _invoker.Invoke(this, action); }).Wait();
}

有什么方法可以使用 NSubstitute 模拟 Action 参数吗?我一直在使用

_proxy.When(x => x.Invoke($args_go_here)).Do(x => _counter++);

但我在制定 Args 表达式时遇到了真正的麻烦。如果我可以简单地创建一个带有 Action 签名的 mock 并将其传递进去,生活就会简单得多,可读性也更高。

正如 David 所评论的那样(请参阅问题评论),我在这里发布了实际解决了我的问题的内容。

问题是测试调用 WCF 服务的代码实际上是 运行,发送正确的东西,等等。我强烈建议任何走这条路的人都以 TDD 风格作为改造单元这样做测试别人的工作代码并不是一种愉快的体验。

在这种情况下,代理不是自动生成的,而是从单个 ProxyBase class 继承的,它暴露了上面引用的 Invoke(Action action) 方法。

public void Invoke(Action<T> action)
{
    Task.Run(async ()=> { await _invoker.Invoke(this, action); }).Wait();
}

我的第一个想法是模拟 Action(我最终确实这样做了)但是这产生了 Service Not Found 错误,非常正确。

最终我得到了(特定的)Proxy Invoker class 的 NSubstitute Automock,它是通过实际将依赖项传递到构造函数中创建的,就像这样 -

var myProxyMock = Substitute.For<MyProxy>(dependency1, dependency2);

并将基本调用方法更改为 Virtual 以便替代方法可以覆盖它,就像这样 -

public virtual void Invoke(Action<T> action)
{
    Task.Run(async ()=> { await _invoker.Invoke(this, action); }).Wait();
}

现在,通过替换 Action,我实际上可以有效地测试周围的代码。

var actionSubstitute = Substitute.For<Action<MyService>>();

并应用相关的 return 值,natch。

所以最终,我们有 -

myProxyMock.When(x => x.Invoke(Arg.Any<Action<MyService>>).Do(x => _counter++);

问题已解决。非常感谢,大卫。

如果您只想要一个间谍而不使用外部跟踪变量(_counter 在 Rich Bryant 的精彩回答中),可以使用 ReceivedCalls() 作为替代。下面的示例使用 System.Linq:

var myAction = Substitute.For<Action<string>>();
myAction.Invoke("abc");

// assert the Action<string> was called once
Assert.Equal(1, myAction.ReceivedCalls().Count());

// assert that the first parameter on the first call was "abc"
Assert.Equal("abc", myAction.ReceivedCalls().First().GetArguments().First());