我仍然关注TDD的部分内容。我有一个新的图书馆,所以这似乎是一个尝试它的好机会。
我在TDD上看到的广告宣传100%的代码覆盖率,但这似乎有点象牙塔,所以我配置了JaCoco要求90%的代码覆盖率给我一些喘息的空间。
我开始使用加载KeyStore的代码。有很多锅炉板代码和许多检查异常。所以从这里开始让我的生活更轻松。一切看起来都很好,我的测试正在通过。但代码覆盖率仅为49%。仔细查看代码,除了我所谓的“不可能的例外”之外,一切都被覆盖了:
public void saveKey(Key key, String alias) {
KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(new SecretKeySpec(key.getMaterial(), "AES"));
try {
keyStore.setEntry(alias, entry, new KeyStore.PasswordProtection(password));
} catch (KeyStoreException e) {
throw new UnexpectedErrorException("Failed to save the key", e);
}
}
在这种特殊情况下,根据文档,如果keyStore尚未初始化,则抛出KeyStoreException。我正在编写防御性编码,并保证keyStore将在此时初始化。因此无法抛出KeyStoreException。但它是一个经过检查的异常,所以我必须处理它,所以我将它包装在一个自定义的RuntimeException中。
问题是我无法在单元测试中触发此错误。事实上,我已尽我所能确保它不会发生。
鉴于此类案例,TDD如何实现神秘的100%覆盖?
我可以模仿KeyStore,但Mockito的建议是“不要嘲笑你不拥有的类型。”所以我宁愿不要。此外,KeyStore依赖于一些静态方法,其中Mockito没有帮助,我不想为一个简单的案例引入PowerMock,我并不相信在这个问题上投入更多的库是一个理想的解决方案。
所以:
我现在预期的解决方案是将我配置的90%代码覆盖率限制降低到40%或50%,直到我有更多的课程来提高我的全部平均覆盖率。但在我这样做之前,有什么我想念的吗?
答案 0 :(得分:4)
与编程的大多数方面一样,测试需要体贴。 TDD是一个非常有用但肯定不足以帮助您获得良好测试的工具。如果你仔细考验,我会期望在80年代或90年代的覆盖率。我会怀疑100%这样的事情 - 它会闻到有人在编写测试以使覆盖数字快乐,但却没有考虑他们在做什么。 - Martin Fowler, 2012
对于您的计划的核心,100%的覆盖率可能是可实现的目标;毕竟,如果没有需要的失败测试,你永远不会引入代码,“重构”不应该引入未使用的分支。
但是与边界交互的代码...... 100%的覆盖率变得更加昂贵,并且最终达到了临界点:
我得到的代码是有效的,而不是测试,所以我的理念是尽可能少地测试以达到给定的置信水平...... - Kent Beck, 2008
如果未选中KeyStoreException,则不会使用此特定示例;检查异常的问题在Java中有点独特。
David Parnas,writing in 1972,给了我们一些关于我们如何解决这个问题的提示。您可以设计解决方案,以便隐藏您使用java.security.KeyStore
的决定。换句话说,您创建一个界面来描述您希望密钥库所具有的API,并将所有代码写入该界面。只有实现需要知道异常管理的细节;只有您的实现需要知道您确定KeyStore异常是不可恢复的。
另一种思考同一想法的方式;我们试图将代码分成两堆: core 包含易于测试的复杂代码; 边界包含难以测试的简单代码。你的边界代码试金石是Hoare:“这么简单,显然没有缺陷”。
使用适合每种情况的测试覆盖率启发式算法。
我现在预期的解决方案是将我配置的90%代码覆盖率限制降低到40%或50%,直到我有更多的课程来提高我的平均覆盖率。
使用棘轮来防止覆盖统计数据的回归是一个好主意。
答案 1 :(得分:0)
我在TDD上阅读的内容宣传了100%的代码覆盖率。
这是一种常见的误解。
当我们进行TDD时,我们不旨在实现100%代码覆盖率。我们的目标是100%需求覆盖率。不,100%的代码覆盖率不意味着100%的需求覆盖率,反之亦然...
很遗憾,我们无法衡量需求覆盖率。所以获得它的唯一方法就是做TDD。
在这种特殊情况下,根据文档,如果keyStore尚未初始化,则抛出KeyStoreException。我是防御性编码,并保证keyStore将在此时初始化。因此无法抛出KeyStoreException。
鉴于此类案例,TDD如何实现神秘的100%覆盖?
我可以嘲笑KeyStore,但是Mockito的建议是“不要嘲笑你不拥有的类型。”#34;
UnitTests验证您的单位行为隔离,这意味着正在测试的任何其他单位您的代码需要由测试双打。有一个讨论,在哪个细节级别我们应该削减我们的单位但"你不拥有的类型" 肯定是不 您的代码的一部分。不嘲笑它们意味着您的测试取决于此外部代码。如果失败,您就不知道您的代码是否存在问题或外部代码。
因此,"不要嘲笑你不拥有的类型。" 是一个相当糟糕的建议。
答案 2 :(得分:0)
实际上TDD与代码覆盖无关 TDD的一个规则是
除非要制作,否则不允许您编写任何制作代码 失败的单元测试通过。
因此,如果您练习测试驱动开发并遵循上述规则,那么您将始终拥有100%的代码覆盖率。
代码覆盖率是单元测试的测量工具,适用于编写生产代码后编写测试的情况。通过代码覆盖,您可以检查"你没有忘记在你的逻辑中为某些案例编写测试。