Don*_*Don 35 c# unit-testing mocking nsubstitute
模拟框架网站中给出的大多数示例都是模拟接口.让我说我正在使用的NSubstitute,他们所有的模拟示例都是模拟界面.
但实际上,我看到一些开发人员嘲笑混凝土类.是否建议模拟混凝土类?
Dav*_*pak 71
从理论上讲,嘲弄具体阶级绝对没有问题; 我们正在测试逻辑接口(而不是关键字interface),并且该逻辑接口是否由class或提供无关紧要interface.
在实践中,.NET/C#使这有点问题.正如您提到的.NET模拟框架,我将假设您仅限于此.
在.NET/C#默认情况下,成员是非虚拟的,因此任何基于代理的模拟行为方法(即从类派生,并覆盖所有成员以执行特定于测试的东西)将不起作用,除非您明确标记成员作为virtual.这会导致一个问题:你正在使用一个模拟类的实例,这个实例在你的单元测试中是完全安全的(即不会运行任何真正的代码),但除非你确定一切都是virtual你可能最终得到的混合使用真实代码和模拟代码运行(如果有构造函数逻辑,这可能会特别成问题,它总是运行,并且如果有其他具体的依赖关系,则会复杂化).
有几种方法可以解决这个问题.
interfaces.这是有效的,也是我们在NSubstitute文档中提出的建议,但是有可能使代码库膨胀的缺点是可能实际上并不需要的接口.可以说,如果我们在代码中找到好的抽象,我们自然会得到我们可以测试的整洁,可重用的接口.我还没有看到它像这样,但是YMMV.:)针对最后一个想法提出的一个常见抱怨是,您正在通过"假"接缝进行测试; 我们正在通常用于扩展代码以改变代码行为的机制之外.需要走出这些机制可能表明我们的设计具有刚性.我理解这个论点,但我已经看到了创建另一个接口的噪音超过了好处的情况.我想这是一个意识到潜在设计问题的问题; 如果您不需要测试中的反馈来突出设计刚性,那么它们就是很好的解决方案.
我要抛弃的最后一个想法是改变我们测试中单位的大小.通常我们将单个类作为一个单元.如果我们有许多内聚类作为我们的单元,并且接口充当了围绕该组件的明确定义的边界,那么我们可以避免必须模拟尽可能多的类,而只是模拟更稳定的边界.这可以使我们的测试变得更加复杂,其优势在于我们正在测试一个具有凝聚力的功能单元,并鼓励他们围绕该单元开发可靠的接口.
希望这可以帮助.
MiF*_*vil 10
更新:
3年后,我想承认我改变了主意.
即使在理论上我仍然不喜欢创建接口只是为了促进模拟对象的创建,在实践中(我使用的是NSubstitute)它更容易使用Substitute.For<MyInterface>()而不是模拟具有多个参数的真实类,例如Substitute.For<MyCLass>(mockedParam1, mockedParam2, mockedParam3),每个参数都应该被模拟分别.NSubstitute文档中描述的其他潜在问题
在我们公司,现在推荐的做法是使用接口.
原始答案:
如果您不需要创建相同抽象的多个实现,请不要创建接口.正如David Tchepak指出的那样,您不希望使用可能实际上不需要的接口来扩展您的代码库.
来自 http://blog.ploeh.dk/2010/12/02/InterfacesAreNotAbstractions.aspx
您是否从类中提取接口以启用松散耦合?如果是这样,您的接口和实现它们的具体类之间可能存在 1:1的关系.这可能不是一个好兆头,违反了 重用抽象原则(RAP).
只有一个给定接口的实现是代码气味.
如果您的目标是可测试性,我更喜欢David Tchepak上面的答案中的第二个选项.
但是我不相信你必须把一切变成虚拟的.只使用虚拟方法就可以替代.我还将在方法声明旁边添加注释,该方法只是虚拟的,以使其可替代单元测试模拟.
但请注意,替换具体类而不是接口有一些限制.例如,NSubstitute
注意:不会为类创建递归替换,因为创建和使用类可能会产生潜在的不必要的副作用
.