如何处理TDD无法访问的异​​常

时间:2017-11-11 02:58:54

标签: java unit-testing tdd

我仍然关注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,我并不相信在这个问题上投入更多的库是一个理想的解决方案。

所以:

  • TDD的100%代码覆盖率是神话吗?
  • 是否有一种技术可以让代码分析识别出所涵盖的代码?

我现在预期的解决方案是将我配置的90%代码覆盖率限制降低到40%或50%,直到我有更多的课程来提高我的全部平均覆盖率。但在我这样做之前,有什么我想念的吗?

3 个答案:

答案 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%的代码覆盖率。

代码覆盖率是单元测试的测量工具,适用于编写生产代码后编写测试的情况。通过代码覆盖,您可以检查"你没有忘记在你的逻辑中为某些案例编写测试。