微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

tdd – 测试正确的东西,避免重复覆盖的技术

考虑以下事件序列:

>你编写了一个功能A()来完成一个工作单元
>您为函数A()编写了一个测试,以确保不存在错误
>你编写使用函数A()的函数B()
>您为函数B()编写了一个测试,以确保不存在错误
4.1功能B()的测试覆盖功能A()因此,您至少有2个测试覆盖了一些相同的功能

问题1:编写函数A()的测试是否值得开始?

>你写了很多代码
>您编写了一个巨大的回归测试,可以端到端地测试程序功能
6.1实际上,这种单一回归测试重复了已经编写的绝大多数测试

问题2:通过遵循这些步骤,代码包含许多不止一次覆盖同一事物的测试.有没有一种技术可以避免这种情况?

假设:

出于这个问题的目的,请假设B做两件事,其中一件完全是A.

void performlifeChoice() { // B()
  if (timeIsRight) {
     askForPromotion();   // A()
  } else {
     goBackToSchool();
  }
}

解决方法

我的一般答案是肯定的,值得为A()编写单元测试,原因有两个:

1)您可能最终从与B()不同的上下文中调用A(),在这种情况下,您将很高兴知道A()正在工作,并避免测试重复.如果你没有单独测试A(),那么对于等效的代码分支,你最终会重写两次实际上相同的测试.

2)相关地,如果你不单独测试A(),你可能最终会让乌龟一直下来.想象一下,函数C()在其中一个分支中调用B(),D()调用C()等…如果你按照仅测试更高级函数的路径,你最终会通过集成测试,必须涵盖越来越多的前置条件.在尽可能小的环境下对各个单元进行测试,原则上可以避免这个问题.

我的答案的一个隐含的结论是,如果永远不会从B()以外的地方调用A(),那么将A()私有化是值得的,并且此时,测试可能变为“可选的”.另一方面,保持该测试可能仍然有用,因为它将帮助您确定当B()失败时,这是因为您打破了A().用Kent Beck的话来说,“如果测试失败了,有多少东西可能出错?答案越接近1,测试的单位越多.” – 进行单元测试非常有用,它可以帮助您精确查明代码中出错的位置.

现在你怎么能用附加的前置条件来测试B(),而不是复制A()的测试?

这就是Mocking / Stubbing或类似技术可以发挥作用的地方.如果你考虑B()正在做什么,它实际上没有执行A(),它充当“协调者”,管理各种路径的条件.在我看来,一个充分的测试是“当TimeIsRight,然后B()应该调用A()”时,A()提供的答案与B()无关,它是A()的责任,你的单元测试封面.

在那个框架中,对B()应该断言的是,在TimeIsRight时调用A(),而不是A()返回.根据代码的具体情况,您可以考虑在B()中使A()“可替换”,例如通过接口,或通过注入代替A()的函数.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐