Ste*_*unn 5 tdd unit-testing mocking inversion-of-control
我尽可能使用 TDD:
然后...
这就是代码覆盖率下降的地方——我感到很难过。
但是,我自由地扩展[CoverageExclude]
了这些具体的类,覆盖率再次上升。
但是,我没有感到悲伤,而是感到肮脏。尽管无法对具体类进行单元测试,但我还是觉得自己在作弊。
我感兴趣的是听你的项目是如何组织的,也就是你怎么安排实际的代码可以对代码进行测试无法进行测试。
我想也许一个不错的解决方案是将不可测试的具体类型分离到它们自己的程序集中,然后禁止在[CoverageExclude]
包含可测试代码的程序集中使用。当在可测试程序集中错误地找到此属性时,这也可以更轻松地创建 NDepend 规则以使构建失败。
编辑:这个问题的本质涉及这样一个事实,即您可以测试使用模拟接口的东西,但您不能(或不应该!)对那些接口的真实实现的对象进行单元测试。下面是一个例子:
public void ApplyPatchAndReboot( )
{
_patcher.ApplyPatch( ) ;
_rebooter.Reboot( ) ;
}
Run Code Online (Sandbox Code Playgroud)
修补程序和重启程序被注入到构造函数中:
public SystemUpdater(IApplyPatches patcher, IRebootTheSystem rebooter)...
Run Code Online (Sandbox Code Playgroud)
单元测试看起来像:
public void should_reboot_the_system( )
{
... new SystemUpdater(mockedPatcher, mockedRebooter);
update.ApplyPatchAndReboot( );
}
Run Code Online (Sandbox Code Playgroud)
这很好用 - 我的 UNIT-TEST 覆盖率为 100%。我现在写:
public class ReallyRebootTheSystemForReal : IRebootTheSystem
{
... call some API to really (REALLY!) reboot
}
Run Code Online (Sandbox Code Playgroud)
我的 UNIT-TEST 覆盖率下降,无法对新课程进行 UNIT-TEST。当然,我会添加一个功能测试并在我有 20 分钟的空闲时间时运行它(!)。
所以,我想我的问题归结为这样一个事实,即接近 100% 的单元测试覆盖率很好。换句话说,能够对系统行为的近 100% 进行单元测试真是太好了。在上面的例子中,补丁程序的行为应该重新启动机器。这是我们可以verify
肯定的。ReallyRebootTheSytemForReal
严格来说,类型不仅仅是行为——它有副作用,这意味着它不能进行单元测试。由于它不能进行单元测试,因此会影响测试覆盖率。所以,
你走在正确的轨道上。您可能可以测试一些具体的实现,例如数据访问组件。针对关系数据库的自动化测试肯定是可能的,但也应该分解到它自己的库中(带有相应的单元测试库)。
由于您已经在使用依赖项注入,因此将此类依赖项重新组合到实际应用程序中应该是小菜一碟。
另一方面,还会存在本质上不可测试的具体依赖项(或者如福勒曾经开玩笑的那样,不可测试)。此类实现应尽可能保持精简。通常,可以将此类依赖项公开的 API 设计为所有逻辑都发生在消费者中,并且实际实现的复杂度非常低。
实现这种具体的依赖关系是一个明确的设计决策,当您做出该决定时,您同时决定不应对此类库进行单元测试,因此不应测量代码覆盖率。
这样的库称为 Humble Object。它(以及许多其他模式)在优秀的xUnit 测试模式中进行了描述。
根据经验,如果代码的圈复杂度为1 ,我接受未经测试的代码。在这种情况下,它或多或少是纯粹的声明性的。实际上,只要不可测试的组件具有较低的环复杂度,它们也是可以的。“低”到底有多低,您必须自己决定。
无论如何,[CoverageExclude] 对我来说似乎是一种气味(在阅读你的问题之前我什至不知道它存在)。
归档时间: |
|
查看次数: |
532 次 |
最近记录: |