在过去的几年,我听很多人在提倡使用自动化测试。例如,如果所有自动化测试用例通过了,那么就可以将代码部署到生产的代码中了。为了让我相信我自己写的代码,我都会先执行一下手动测试。因此在过去的几年对尤其关注我手动测试代码而发现的 bug。我的结论是:手动测试依然是必须的。
我是如何测试的
在工作中,我们都有生产代码和测试代码。每个开发者也都可以在他们自己的机器上运行这些代码。当我开发一个新功能时,单元测试和集成测试我都会编写。典型的,我的单元测试会专注于我的算法的准确性。例如计算金额,它有很多的参数,例如总额,阈值,差额等。这个单元测试可以确保输入不同范围的金额计算是正确的。集成测试则是使用更多的基础架构,例如 web 服务器 和数据库。这些测试可以确保当有 API 调用时可以返回正确的数值。他会测试很大测试链,将所有的测试拼接在一起。单元测试涵盖了计算的所有细节,集成测试只是确保计算在其预期的上下文中工作。
当我完成一个新功能时,单元测试和集成测试都会通过。然而,在我发布这个测试之前,我会去执行一些手动测试。在我的机器上启动整个系统所有部分,包括GUI。这这个情况中我尝试运行这个新特性。我会运行这个新特性主要功能并检查是否如预期一样的工作。我注意任何不寻常或者意外的事情发生。我也会检查日志,看每个地方是否如预期一样。如果发现任何的不寻常,就要深挖它。一旦这一步完成了,我认为这个新功能算是结束了。
知道几年前,我才叫这种方式为手动测试。但是我阅读了 Elisabeth Hendrickson 写的 Explore It!我现在认为探索测试很好的描述我做的事情。这个本书开始就描述了检查和探索的区别。检查是当你知道程序在给定的环境下应该如何运行,并且你验证了它。最好的做法是通过自动化测试来完成。另一方面探索更加的流畅,你尝试一些事情,让你观察到的来指导你下一步该做什么,并保持警惕潜在的bug。
为什么手动测试是需要的
所以,探索测试是必须的?在过去的今年我断断续续的思考这个问题。我认为它是有用且必须的有两个主要原因。一个原因更有哲理性,另一个则是基于我的经验。
哲理性
我认为区分检查和探索是非常重要的。如果你认为自动化测试已经够了,那么基本上你是在说检查已经够了,并且我们不需要探索。在我看来,这等同于说我们可以提出所有我们需要的测试用例,我不认为这是可行的。
这和一般的软件开发类似。在开始之前想出整个设计是不可能的。相反,你需要才去迭代的方法,一路学习,并在下一次开发程序总融入新的知识。这与不运行完整的程序就想测试出所有情况,是不可能的。
探索性测试和自动化集成测试与集成测试和单元测试之间是类似的。集成测试需要确保在单元测试中的逻辑是服务更大的情况。探索性测试再最高的层级中——整个系统。他们需要确保所有事情被自动化测试到并且是有意义的,不仅仅是自身,而是整体的一部分。
所以,如果你认为自动化测试已经足够了,你也可以说不需要探索性测试了。我认为这是错误的——我认为检查和探索都是需要的,而不仅仅是检查而已。
探索
过去的几年,我尤其关注通过手动测试而发现的bug。我尝试去覆盖所有的测试用例,但是当我在完整的系统中探索时仍然发现bug。类似于我是如何追踪到有意思的bug,我一直在跟踪这些发现漏洞的实例,这些漏洞是在检查时漏掉的,但在探索时被发现了。总的来说,它大约每月发生一次。这里有两个例子:
1.string 对 float。我添加了一个功能,可以根据不一致的金额来停止某些保证金追缴。在一个用例中,这意味着检查通知金额是否大于抵押品金额。为不同的场景编写自动测试很简单。然而,当通过GUI尝试一个用例时,我得到了一个令人惊讶的结果。测试“if 8 > 9”看起来是true的!在调查时,我发现python代码将字符串“8”与浮点数9进行了比较。在python 2.7中,这不是fale,而是返回true!出于历史原因,当时的GUI将所有值作为字符串发送。但在后端,数字被存储为浮点数。这就是为什么类型不匹配发生在比较中,导致错误的结果。在我所有的自动测试中,我都假设来自GUI的API调用是浮动的,但结果证明这是不正确的。
2.什么不该发生。以前,协议货币和协议类型在一个位置上设置一次,此后不能更改。我们希望在没有追加保证金通知的情况下,能够更改这些值。同样,代码很容易编写和测试。但是,当在整个系统上测试时,我注意到当我只更改协议货币时,协议类型被清除为 None。这是错误的。在我的测试中,我断言每个变化都有预期的效果。然而,我没有断言其他值没有改变。但是当在GUI中尝试时,很明显不止一个值被改变了。
这里的重点并不是不可能提出捕获这些有bug的自动测试。关键是在这些情况下,我没有想到它们。你同样可以争辩说,你的代码中永远不会有任何错误,因为你可以事先考虑这些情况,并处理它们。理论上这是可能的,但实际上错误是会发生的。探索性测试是捕获自动测试中难以捕获的bug的相对成本较低的方法(因为你没有考虑案例)。根据我的经验,当把系统作为一个整体来使用时,测试用例中难以考虑的bug通常会变得很明显。
我认为这些错误的一个原因是自动测试集中在代码上,而探索性测试集中在系统的行为上。我在手动测试阶段发现的许多错误是由我做出的不真实的假设引起的。我还注意到手动测试经常让我想到要运行的附加测试。
Elisabeth Hendrickson 也有类似的经验。早在 Explore It!中她引用(并同意)一位同事的观点:“不管我们写了多少测试,不管我们执行了多少案例,当我们离开脚本时,我们总是会发现最严重的错误”。这也是我的经验。
笔记
结论
我目前对测试的看法是手动测试对自动化测试是有价值的补充。我的经验是,我经常以这种方式发现严重的错误,并且做这个测试的成本很低。我有兴趣听听其他的观点和经验。你如何做测试?你同意还是不同意我的结论?请在评论中告诉我。
开始对这个标题挺感兴趣的,但是看着看着发现他说的和实际上关联性不强,以后选文章要选有意义的,实用性强的。