C# 如何使用AutofacContrib.NSubstitute监视测试中的类
我正在使用NSpec框架、AutofacContrib.NSubstitute v3.3.2.0、NSubstitute v1.7.0.0(目前最新版本是1.8.2)在类库项目中运行单元测试 测试实例下的类是使用C# 如何使用AutofacContrib.NSubstitute监视测试中的类,c#,unit-testing,nsubstitute,automocking,C#,Unit Testing,Nsubstitute,Automocking,我正在使用NSpec框架、AutofacContrib.NSubstitute v3.3.2.0、NSubstitute v1.7.0.0(目前最新版本是1.8.2)在类库项目中运行单元测试 测试实例下的类是使用AutoSubstitute构建的,以便自动模拟所有需要的依赖项 AutoSubstitute autoSubstitute = new AutoSubstitute(); MainPanelViewModel viewModel = autoSubstitute.Resolve<
AutoSubstitute
构建的,以便自动模拟所有需要的依赖项
AutoSubstitute autoSubstitute = new AutoSubstitute();
MainPanelViewModel viewModel = autoSubstitute.Resolve<MainPanelViewModel>();
因此,对于测试预期,我需要检查(spy)实例是否调用了基本方法:
viewModel.Received().ActivateItem(Arg.Any<SomeSpecificScreenType>());
viewModel.Received().ActivateItem(Arg.Any());
问题是:当我尝试这样做时,在运行时NSubstitute抱怨我只能对使用Substitute.For()创建的对象运行Received()
。我还快速检查了AutofacContrib.NSubstitute源代码,但我找不到一种方法来获取带有automocking的实例,同时以某种方式将其包装到spy对象或类似的东西中
我还认为,Substitute.ForPartsOf()
可能会有所帮助,但在NSubstitute v1.7.0中似乎找不到这种方法
为完整起见,以下是NSSubstitute完整错误:
nssubstitute扩展方法(如.Received())只能对使用Substitute.For()和相关方法创建的对象调用
所以,实际问题并没有真正解决:只是问题本身消失了
为了检查行为是否正确,我回忆起我也可以从基类中使用ActiveItem
公共属性,因此我停止使用Receive()
,返回到简单的值比较
不过,为了将来的参考,我没有找到一种方法用这些库来监视被测试的类。我知道应该避免监视测试中的类,但与许多事情一样,有时您需要这样做
HTH为了完整性,我做了一些实验,用替换了NSubstitute的部分
ForPartsOf
的工作方式本质上是定义一个新的类,该类从用作模板的类继承。这就限制了模拟框架可以拦截的方法是定义为abstract
或virtual
的方法。如果要用自己的类从类继承,那么修改类的行为也会受到同样的限制
根据这些信息,让我们看看您的问题。您要拦截此呼叫:
base.ActivateItem(nextScreen);
因此,由于上述限制,为了能够拦截对ActivateItem
的调用,必须在基类中将该方法标记为virtual
。如果不是,那么在不更改应用程序结构的情况下,您将无能为力
如果该方法标记为virtual
,则可以使用NSubstitute拦截它,但只能在调用NSubstitute实现时进行拦截。这是通过普通的方法分派来实现的,因为在调用虚拟方法时会调用它的最高级别实现(由NSubstitute提供的实现)。但是,当您通过base
引用调用该方法时,它不起作用
所以,虽然你可以截取这个:
ActivateItem(nextScreen)
base.ActivateItem(nextScreen);
您无法截获此消息:
ActivateItem(nextScreen)
base.ActivateItem(nextScreen);
您在代码中使用base.ActivateItem
的事实表明,您的被测类有自己的方法实现,您不想调用该方法,因此使用当前的工具无法实现您想要做的事情。这就是为什么你能找到一份工作是件好事
与大多数其他模拟框架处于相同的情况,包括。例外情况是,它使用一种完全不同的方式来拦截方法调用,这意味着它可以做其他框架无法做的事情。您应该真正测试基类中ActivateItem
的行为是否发生,而不是检查方法本身是否被调用(本质上是一个实施细节)。有什么原因你不能这样做吗?substitute.ForPartsOf
可能会有帮助,但模拟你正在测试的类通常不是一个好主意。你想要测试的方法也需要是虚拟的,因此在不测试它的情况下,我怀疑显式调用base.ActivateItem
是否会实际触发替换y、 测试该方法的功能可能更容易。我大体上完全同意这一点。我打算回答您的评论,即这是一个CaliburnMicro视图模型,鉴于我们进入UI视图领域,检查ActivateItem
行为将很难。但我刚刚回忆起,还应该有一个ActivateItem代码>属性…所以现在我要检查一下!现在我想知道我是否应该结束这个问题,或者你可以发布一个自我回答,并在时间过去后接受它…大约24小时谢谢你的调查,我认为这对每个来到这个线程的人来说都是一个有用的澄清。只有2位:我知道te base.Xxxx只是为了清楚起见,在代码中,我只是调用ActivateItem
,我没有覆盖它(尽管它是可覆盖的)。此外,在切换到NSubstitute之前,我使用Moq和Moq.AutoMock,并使用mocker.CreateSelfMock()
成功地进行了监视,然后使用Mock.Get(viewModel)。验证(vm=>vm.ActivateItem(It.IsAny())
@superjos有趣的是,是base.Xxx
调用让它变得有趣。如果这只是一个普通的虚拟调用,那么你可以使用Received
作为部分替换,正如你在问题中所说的那样,它应该可以正常工作。NSubstitute的发行说明可能有点错误,但它们似乎说,对于shoul的部分d在1.7.0版中…但是,再一次,被测试的类是由AutofacContrib.NSubstitute用automocking实例化的,我看不到它的任何方法可以返回部分替换