是否建议模拟具体类? [英] Is it recommended to mock concrete class?

查看:28
本文介绍了是否建议模拟具体类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

mocking framework 网站中给出的大多数示例都是模拟接口.假设我目前使用的 NSubstitute,他们所有的模拟示例都是模拟界面.

Most of the examples given in mocking framework website is to mock Interface. Let say NSubstitute that I'm currently using, all their mocking examples is to mock interface.

但实际上,我看到了一些开发人员模拟的具体类.是否建议模拟具体类?

But in reality, I saw some developer mock concrete class instead. Is it recommended to mock concrete class?

推荐答案

理论上,mock 一个具体的类绝对没有问题;我们正在针对逻辑接口(而不是关键字interface)进行测试,并且该逻辑接口是由class 还是由interface.

In theory there is absolutely no problem mocking a concrete class; we are testing against a logical interface (rather than a keyword interface), and it does not matter whether that logical interface is provided by a class or interface.

在实践中 .NET/C# 使这有点问题.正如您提到的 .NET 模拟框架,我假设您仅限于此.

In practice .NET/C# makes this a bit problematic. As you mentioned a .NET mocking framework I'm going to assume you're restricted to that.

在 .NET/C# 中,默认情况下成员是非虚拟的,因此任何基于代理的模拟行为方法(即从类派生,并覆盖所有成员以执行特定于测试的内容)都将不起作用,除非您明确将成员标记为 virtual.这会导致一个问题:您正在使用一个模拟类的实例,该实例在您的单元测试中是完全安全的(即不会运行任何真正的代码),但除非您确保一切都是 virtual 您最终可能会混合运行真实代码和模拟代码(如果存在始终运行的构造函数逻辑,则这可能尤其成问题,并且如果有其他具体的依赖项需要新建,则情况会复杂化).

In .NET/C# members are non-virtual by default, so any proxy-based methods of mocking behaviour (i.e. derive from the class, and override all the members to do test-specific stuff) will not work unless you explicitly mark the members as virtual. This leads to a problem: you are using an instance of a mocked class that is meant to be completely safe in your unit test (i.e. won't run any real code), but unless you have made sure everything is virtual you may end up with a mix of real and mocked code running (this can be especially problematic if there is constructor logic, which always runs, and is compounded if there are other concrete dependencies to be new'd up).

有几种方法可以解决这个问题.

There are a few ways to work around this.

  • 使用接口.这是有效的,也是我们在 NSubstitute 文档 中的建议,但有可能使您的代码库膨胀的缺点带有实际上可能不需要的接口.可以说,如果我们在代码中找到良好的抽象,我们自然会得到整洁、可重用的接口,我们可以对其进行测试.我还没有看到它像那样成功,但是 YMMV.:)
  • 努力让一切变得虚拟.一个有争议的缺点是,当我们真的只想更改整个类的行为以进行测试时,我们建议所有这些成员都旨在成为我们设计中的扩展点.它也不会停止构造函数逻辑的运行,如果具体类需要其他依赖项,它也无济于事.
  • 通过Virtuosity 插件 之类的工具重写程序集,用于Fody,可用于将程序集中的所有类成员修改为虚拟.
  • 使用基于非代理的模拟库,例如 TypeMock (paid)JustMock (付费)Microsoft Fakes (需要 VS Ultimate/Enterprise,尽管它的前身 Microsoft Moles,是免费的)Prig (免费 + 开源).我相信这些能够模拟类的所有方面,以及静态成员.
  • Use interfaces. This works and is what we advise in the NSubstitute documentation, but has the downside of potentially bloating your codebase with interfaces that may not actually be needed. Arguably if we find good abstractions in our code we'll naturally end up with neat, reusable interfaces we can test to. I haven't quite seen it pan out like that, but YMMV. :)
  • Diligently go around making everything virtual. An arguable downside to this is that we're suggesting all these members are intended to be extension points in our design, when we really just want to change the behaviour of the whole class for testing. It also doesn't stop constructor logic running, nor does it help if the concrete class requires other dependencies.
  • Use assembly re-writing via something like the Virtuosity add-in for Fody, which you can use to modify all class members in your assembly to be virtual.
  • Use a non-proxy based mocking library like TypeMock (paid), JustMock (paid), Microsoft Fakes (requires VS Ultimate/Enterprise, though its predecessor, Microsoft Moles, is free) or Prig (free + open source). I believe these are able to mock all aspects of classes, as well as static members.

针对最后一个想法的常见抱怨是,您正在通过假"接缝进行测试;我们正在超越通常用于扩展代码的机制来改变我们代码的行为.需要超出这些机制可能表明我们的设计很僵化.我理解这个论点,但我见过创建另一个接口的噪音超过好处的情况.我想这是意识到潜在的设计问题的问题;如果您不需要来自测试的反馈来突出设计刚性,那么它们就是很好的解决方案.

A common complaint lodged against the last idea is that you are testing via a "fake" seam; we are going outside the mechanisms normally used for extending code to change the behaviour of our code. Needing to go outside these mechanisms could indicate rigidity in our design. I understand this argument, but I've seen cases where the noise of creating another interface/s outweighs the benefits. I guess it's a matter of being aware of the potential design issue; if you don't need that feedback from the tests to highlight design rigidity then they're great solutions.

我要抛出的最后一个想法是在我们的测试中改变单元的大小.通常我们有一个单一的类作为一个单元.如果我们有许多内聚类作为我们的单元,并且有接口作为围绕该组件的明确定义的边界,那么我们可以避免模拟尽可能多的类,而只是模拟一个更稳定的边界.这会使我们的测试变得更加复杂,优势在于我们正在测试一个有凝聚力的功能单元,并被鼓励围绕该单元开发可靠的接口.

A final idea I'll throw out there is to play around with changing the size of the units in our tests. Typically we have a single class as a unit. If we have a number of cohesive classes as our unit, and have interfaces acting as a well-defined boundary around that component, then we can avoid having to mock as many classes and instead just mock over a more stable boundary. This can make our tests a more complicated, with the advantage that we're testing a cohesive unit of functionality and being encouraged to develop solid interfaces around that unit.

希望这会有所帮助.

这篇关于是否建议模拟具体类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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