如何使用AutofacContrib.NSubstitute监视被测类 [英] How to spy the class under test with AutofacContrib.NSubstitute

查看:159
本文介绍了如何使用AutofacContrib.NSubstitute监视被测类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用NSpec框架,AutofacContrib.NSubstitute v3.3.2.0,NSubstitute v1.7.0.0(目前最新为1.8.2)在类库项目中运行单元测试.

被测类实例是使用AutoSubstitute构建的,以便自动模拟其所需的所有依赖项.

AutoSubstitute autoSubstitute = new AutoSubstitute();

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

如果工作正常,则我的被测类"有时会使用一些特定的输入参数来调用其基类方法之一(该基类不在我的控制范围内):

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

因此,出于测试期望,我需要检查(间谍)实例是否调用基本方法:

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

这是问题所在:当我尝试执行此操作时,NSubstitute在运行时抱怨我只能对使用Substitute.For<>()创建的对象运行Received().我还快速检查了AutofacContrib.NSubstitute源代码,但是找不到通过自动模拟获取实例的方法,同时又以某种方式 wrap 将其包裹在一个间谍对象中. >

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

出于完整性考虑,以下是NSubstitute完整错误:

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

解决方案

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

ForPartsOf的工作方式实质上是定义一个新类,该类继承自您用作模板的类.这限制了模拟框架可以拦截定义为abstractvirtual的方法的含义.如果要使用自己的类从类中继承,则修改类的行为也有同样的限制.

获取此信息,让我们看一下您的问题.您想拦截此呼叫:

base.ActivateItem(nextScreen);

因此,由于上述限制,要使您能够拦截对ActivateItem的调用,必须在基类中将该方法标记为virtual.如果不是,那么什么都没做,而无需更改应用程序结构.

如果该方法被标记为virtual,则可以使用NSubstitute 对其进行拦截,但是您只能在调用NSubstituted实现的情况下进行此操作.这可以通过常规方法分派进行工作,因为在调用虚拟方法时,将调用虚拟方法的最高级别实现(由NSubstititute提供).但是,当您通过base引用调用该方法时,该方法不起作用.

因此,尽管您可以拦截此内容:

ActivateItem(nextScreen)

您根本无法拦截它:

base.ActivateItem(nextScreen);

您在代码中使用base.ActivateItem的事实表明,您的被测类具有自己不想调用的方法的实现,因此,使用当前的工具,您将无法实现所要实现的目标试图做.这就是为什么您发现变通办法.

与大多数其他模拟框架(包括 Moq )处于同一情况.例外是 TypeMock ,它使用完全不同的方法拦截方法调用的方式,这意味着它可以完成其他框架无法完成的工作.

I'm running unit tests in a class library project with NSpec framework, AutofacContrib.NSubstitute v3.3.2.0, NSubstitute v1.7.0.0 (the latest as of now is 1.8.2).

The Class Under Test instance is built with AutoSubstitute, in order to automock all its needed dependencies.

AutoSubstitute autoSubstitute = new AutoSubstitute();

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

If working properly, my Class Under Test at some point will invoke one of it's base class methods with some specific input parameter (the base class is out of my control):

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

So, for test expectation, I need to check (spy) that the instance invokes the base method:

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

Here's the problem: when I try to do this, at runtime NSubstitute complains that I can only ran Received() against an object created with Substitute.For<>(). I also checked quickly AutofacContrib.NSubstitute source code, but I could not find a way to obtain the instance with automocking and at the same time wrap it somehow in a spy object or something like that.

I also thought that maybe Substitute.ForPartsOf<>() could be helpful, but that method does not seem to be found in NSubstitute v1.7.0.

For sake of completeness, here's NSubstitute full error:

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

解决方案

For completeness, I did some experimenting with NSubstitute's partial substitutions with ForPartsOf.

The way ForPartsOf works is essentially to define a new class that inherits from the class you're using as a template. This restricts what it is that the mocking framework can intercept to methods that are either defined as abstract or virtual. This is the same limitation you would have for modifying the behaviour of a class if you were to inherit from it with your own class.

Taking this information, lets look at your problem. You want to intercept this call:

base.ActivateItem(nextScreen);

So, because of the limitations above, for you to be able to intercept a call to ActivateItem, the method has to be marked as virtual in the base class. If it's not, there's nothing you can do without changing the application structure.

If the method is marked as virtual, then you can intercept it with NSubstitute but you can only do it if the NSubstituted implementation is called. This works through normal method dispatch, because the highest level implementation of a virtual method is called (the one provided by NSubstitute) when you invoke it. However, it doesn't work when you're calling the method via the base reference.

So, whilst you could intercept this:

ActivateItem(nextScreen)

You simply can't intercept this:

base.ActivateItem(nextScreen);

The fact that you're using base.ActivateItem in your code suggests that your class under test has its own implementation of the method that you don't want to call, so with your current tools you can't achieve what you were trying to do. Which is why it's a good thing that you found a workaround.

You're in the same situation with most other mocking frameworks, including Moq. The exception is TypeMock, which uses a totally different way to intercept method calls which means that it can do things that other frameworks simply can't.

这篇关于如何使用AutofacContrib.NSubstitute监视被测类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆