什么是单元测试
是指对软件中的最小可测试单元进行检查和验证
说人话就是对实现业务的不同 method / function 进行测试
举个例子,假设我们的代码分层是 Controller —> Service —> Model,那么我们进行测试的部分就是 Service
为什么我们需要单元测试
很多刚刚接触单元测试的玩家都会有这个疑问,具体的代码我 run 一次就可以验证了,比如一个 Service 的方法大可以写一个 script 去调用执行,一个请求就更简单,直接发起请求看返回就可以,那为什么还要写代码去测试代码,甚至在有些语言中测试还成了一等公民
确实,实际 run 一下是最全面稳妥的测试方式,但是这样难以解决以下几个问题
- 可持续性
- 回归测试
- 外部依赖调用
可持续性,是指上文提到的 script 调用或者接口请求,无法形成长期的测试方式,当然,如果你愿意将所有的 test scirpt 和请求 curl 都保留下来,这本质上就成了一种青春版的单元测试
回归测试,是指我们在修改了其他功能模块时,可能会对旧有代码造成影响,理论上来讲,我们对系统进行了任何修改都应该重新验证系统的可靠性,实际上这也是测试可持续的延伸
外部依赖调用,请想象一个场景,一个业务模块需要调用外部服务,而这个外部服务是未完成的 / 收费的 / 有白名单限制的…种种原因你难以直接调用到,此时你就需要 Mock 来模拟外部依赖的返回,实际上这不是单元测试可以解决的问题,但是 Mock 一般只为测试服务
总的来说,单元测试是一件开发阶段较高投入但是具有长期收益的事情
为了单元测试,应该准备什么
首先最重要的,要保证自己的代码具有可测试性,这一点要展开有些复杂,我搜集了一番,简单整理如下
- 广泛使用依赖注入,令自己的类 / 方法的依赖具有可替换性
- 稳定性,在给定的情况下,程序输出应是可预测的
- 代码保持低耦合
- 谨慎设计私有方法,在 PHP 中,一般情况下私有方法只能通过反射的方式去测试
- 谨慎使用静态方法,静态方法本身并非面向对象的而是面向过程的,和全局变量类似,静态方法不该依赖于外部环境,系统时间,网络等
- 完整的数据库迁移,因为单元测试执行每一个 case 都需要一个空白的环境并重新构建基境
这需要我们在编写代码的时候就开始考虑,我相信这也有利于书写出更易读的代码
其次是一些基础的概念,这部分可以去百度了解一下
- 单元测试框架,PHP 中就是 phpunit
- Mock,上文提到过,难以调用的外部依赖是应当被 Mock 掉的,PHP 提供了 Mockery 库来 mock 任意的类
- 依赖注入,服务于 Mock
- 断言,判断输出是否和预期一致的方法
准备好了这些基础概念,之后我们将基于 Laravel 和 Hyperf 各自书写一个完整的单元测试