如何使用 AutofacContrib.NSubstitute 监视正在测试的 class

How to spy the class under test with AutofacContrib.NSubstitute

我正在运行 class 库项目中使用 NSpec 框架进行单元测试,AutofacContrib.NSubstitute v3.3.2.0,NSubstitute v1.7.0.0(最新版本现在是 1.8.2).

Class Under Test 实例是用 AutoSubstitute 构建的,以便自动模拟其所有需要的依赖项。

AutoSubstitute autoSubstitute = new AutoSubstitute();

MainPanelViewModel viewModel = autoSubstitute.Resolve<MainPanelViewModel>();

如果工作正常,我的 Class Under Test 在某些时候会调用其中一个带有特定输入参数的基本 class 方法(基本 class 不在我的控制范围内):

// ...
base.ActivateItem(nextScreen);
// ...

因此,为了测试预期,我需要检查(侦测)该实例是否调用了基本方法:

viewModel.Received().ActivateItem(Arg.Any<SomeSpecificScreenType>());

问题是:当我尝试这样做时,在运行时 NSubstitute 抱怨说我只能 运行 Received() 反对使用 Substitute.For<>() 创建的对象。我也快速检查了 AutofacContrib.NSubstitute 源代码,但我找不到一种方法来获取带有自动模拟的实例,同时 wrap 它以某种方式在间谍对象或类似的东西中那。

我也认为 Substitute.ForPartsOf<>() 可能会有帮助,但 NSubstitute v1.7.0 中似乎没有找到该方法。

为了完整起见,这里是 NSubstitute 的完整错误:

NSubstitute extension methods like .Received() can only be called on objects created using Substitute.For() and related methods.

所以,实际问题并没有真正解决:只是问题本身消失了。

为了检查正确的行为,我记得我还可以求助于基数 class 的 ActiveItem public 属性,所以我停止了使用 Receive() 并返回到简单的值比较。

不过,为了将来参考,我没有找到一种方法来监视 Class Under Test 与这些库。我知道应该避免监视正在测试的 class,但与许多事情一样,有时您需要这样做。

HTH

为了完整起见,我用 ForPartsOf 对 NSubstitute 的部分替换做了一些实验。

ForPartsOf 的工作方式本质上是定义一个新的 class,它继承自您用作模板的 class。这将模拟框架可以拦截的内容限制为定义为 abstractvirtual 的方法。如果您要用自己的 class.

从它继承,这与修改 class 的行为所具有的限制相同。

根据这些信息,让我们看看您的问题。你想拦截这个电话:

base.ActivateItem(nextScreen);

因此,由于上述限制,为了能够拦截对 ActivateItem 的调用,该方法必须在基础 class 中标记为 virtual。如果不是,那么在不更改应用程序结构的情况下,什么都做不了

如果该方法被标记为virtual,那么您可以使用NSubstitute 拦截它但是您只能在调用NSubstituted 实现时执行此操作。这是通过正常的方法分派来工作的,因为当你调用它时,虚方法的最高层实现被调用(由 NSubstitute 提供)。但是,当您通过 base 引用调用方法时它不起作用。

所以,虽然你可以拦截这个:

ActivateItem(nextScreen)

你根本无法拦截这个:

base.ActivateItem(nextScreen);

您在代码中使用 base.ActivateItem 的事实表明您的 class 被测有其自己不想调用的方法的实现,因此您当前的您无法实现您想要做的事情的工具。这就是为什么您找到 .

是一件好事

您与大多数其他模拟框架(包括 Moq. The exception is TypeMock)处于相同的情况,它使用完全不同的方式来拦截方法调用,这意味着它可以做其他框架根本做不到的事情。