最近学习了一点PHP单元测试的东西,这里做一个简单的整理。

什么是单元测试?

单元测试将你的程序代码进行隔离并证明这个单个部件是正确的。通常情况下,单元测试是非常小、快并且简短的代码片段——比如一个方法。

这样做有什么好处?

可以保证代码的简洁,增强代码的可读性,同时还有利于编写更多有意义的代码,能减少代码的bug数量等等好处。

从手动测试到单元测试:

之前测试(调试)代码的时候基本是这样的:编写代码——>打开浏览器——>给定不同的输入条件——>看输出的结果是否符合预期的结果。对,这个就是手动测试,是对功能设计的测试,不是对我们的代码进行测试。

现在我们想要做的是自动测试,不再像上面说的那样死板的东西。有两种主要的方法可以达到如此效果——TDD(test-driven development)测试驱动开发 和 BDD(behavior-driven development)行为驱动开发。

TDD是非常简短的,在编写业务代码之前编写测试用例,这种方式依赖于单元测试。正如前面提到的,单元测试是简短的、容易编写的代码片段,也就是说你的业务代码要确保被单元测试的代码所覆盖。TDD的大概流程是针对于每个功能点抽象出接口代码——>编写单元测试的代码——>实现接口——>运行单元测试代码,循环此过程,直到整个单元测试都通过。这样做的好处是:减少代码逻辑的错误,尽可能减少项目中的bug。当然不是所有的项目都适合TDD这个方式。那使用TDD都是在什么情景下呢?

  • (1) 对项目的需求足够的了解
  • (2)项目本身的复杂度和依赖性,如果一个项目极其复杂,模块之间又是相互依赖,这种情况下,TDD反正会增大拆分接口和编写测试用例的工作量。

BDD,另一种单元测试的方式,就必须要对某些行为要有具体的期望,这样测试才会有意义!

一些讨论

写到这里,对每样东西每个人的看法都是不一样,对单元测试也是这样。也有人反对单元测试,每个东西不会是十全十美,当然了我写这篇文章肯定要夸单元测试的好处。现在不妨来讨论讨论。

编写测试用例浪费很多时间?

首先需要说明的是,编写测试用例代码(额外的代码)显而易见是需要花时间的。对的,这个不只是花费时间的唯一因素。当我们编写完一个项目的业务逻辑的代码,过了好几个月,回过头来去看我们自己的代码,就要花费很长时间去理解我当初为什么要这样编写而不那样编写。也有可能是花费时间去理解别人的代码。fix bug的时候也需要花费时间在自己的代码中穿行。如果我们使用第三方的一些库,库被修改,我们也需要花很多时间review我们的代码……这些时间加起来是不是要比编写测试用例的代码还要多得多。
甚至还有人抱怨说,“我编写了测试用例的代码,并没有找到bug,这不是浪费时间?是什么?”既然你编写了测试用例,当你修改了某个部分的代码时,就可以帮你检查出来。至少这样你不需要手动测试你哪些修改过的代码。

我的代码,我了解,我不需要测试?

有的时候,我们第一次编写业务逻辑代码是非常清晰的,但过了一段时间我们总要花费一点时间来理解当初编写业务逻辑的代码。我们写的测试用例的方法通常有五六行代码组成,但这些方法可用于不同的服务器、不同版本的脚本语言环境。

不可能测试所有的差异?

这个答案是肯定的,不可能测试所有的差异。但一个单元能够帮你展示你所能覆盖测试的差异。如果你能够根据非单元测试覆盖测试出的差异来保证应用的正常运行,你可以扩展你的测试范围。举个栗子,当你用某个确定的整数作为输入条件成功跑通了程序,你可以扩展这个测试范围,用0、非整数或者其他作为输入条件进行测试。

编写测试用例难?

编写测试用例很简单,编写nice的测试用例难。这需要一个实践的过程!

总之,学习单元测试是需要花费时间的。即便你是第一次使用单元测试,但这个学习曲线很短而且是有回报的。何乐而不为呢?

补充几点:

测试功能

编写代码的时候,print_r()var_dump()确实是一个非常友好的测试,但是没有真正达到测试的目的。手动测试也是比较花费时间、无聊的。当你因为某个功能要修改的旧代码、或因为某个bug要修改某块代码的时候,此时你需要一些东西来测试你的代码是否还有你想要的功能。手动测试前,通常要列出所有输入值(如:负数、0、字符串等等)如果某次测试你忘记了测试字符串的情况就将代码部署到生产环境了。单元测试,不会发生这种事情。

重构

对变量、方法重命名以增强代码的可读性,此时要小心重命名会不会带来新的bug和冲突。有的时候,有些代码跟目标功能一点关联都没有,显然,可以删除这样的代码,但你需要检查剩余的代码还是能够正常工作的。

代码质量

测试运行是隔离的。进行测试就是在证明当前代码片段不依赖其他代码块。