1

我正在使用 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>();

如果工作正常,我的被测类在某些时候会调用它的基类方法之一,并带有一些特定的输入参数(基类不在我的控制范围内):

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

因此,对于测试期望,我需要检查(spy)该实例是否调用了基本方法:

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

这是问题所在:当我尝试这样做时,在运行时 NSubstitute 抱怨我只能Received()针对使用Substitute.For<>(). 我还快速检查了 AutofacContrib.NSubstitute 源代码,但我找不到通过自动模拟获取实例并同时以某种方式将其包装在间谍对象或类似对象中的方法。

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

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

NSubstitute 扩展方法如 .Received() 只能在使用 Substitute.For() 和相关方法创建的对象上调用。

4

2 回答 2

1

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

为了检查正确的行为,我记得我也可以ActiveItem从基类中使用公共属性,所以我停止使用Receive()并回到简单的值比较。

尽管如此,为了将来参考,我没有找到一种方法来使用这些库来监视被测类。我知道应该避免监视被测类,但与许多事情一样,有时你需要这样做。

高温高压

于 2015-06-25T15:43:32.627 回答
1

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

工作方式ForPartsOf本质上是定义一个新类,该类继承自您用作模板的类。这将模拟框架可以拦截的内容限制为定义为abstract或的方法virtual。如果您要使用自己的类从它继承,这与修改类的行为相同的限制。

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

base.ActivateItem(nextScreen);

因此,由于上述限制,为了能够拦截对 的调用ActivateItem,必须将该方法标记为virtual在基类中。如果不是,那么如果不更改应用程序结构,您将无能为力

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

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

ActivateItem(nextScreen)

你根本无法拦截这个:

base.ActivateItem(nextScreen);

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

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

于 2015-06-25T23:08:59.760 回答