如果你无法量化它,就无法改进它。
经过讨论,大家均认为单元测试是有帮助的,接下来就是如何写的问题了。
单元测试的意义
1.帮助理解需求
单元测试应该反映 Use Case,把被测单元当成黑盒测试其外部行为。
2.提高实现质量
单元测试不保证程序做正确的事,但能帮助保证程序正确地做事,从而提高实现质量。
3.测试成本低
相比集成测试、验收测试,单元测试所依赖的外部环境少,自动化程度高,时间短,节约了测试成本。
4.反馈速度快
单元测试提供快速反馈,把bug消灭在开发阶段,减少问题流到集成测试、验收测试和用户,降低了软件质量控制的成本。
5.利于重构
由于有单元测试作为回归测试用例,有助于预防在重构过程中引入 bug。
6.文档作用
单元测试提供了被测单元的使用场景,起到了使用文档的作用。
7.对设计的反馈
一个模块很难进行单元测试通常是不良设计的信号,单元测试可以反过来指导设计出高内聚、低耦合的模块。
单元测试环境搭建
phpunit 在lumen中可用参考地址:
phpstorm 使用参考地址:
单元测试的原则
A:Automatic(自动化)
I:Independent(独立性)
R:Repeatable(可重复)
由原则推出被测方法应该具有以下特征
应该
- 每个函数都应该原子化,不受上下游影响,独立可重复执行
- 函数如果有依赖关系,使用 @depends 来指定依赖关系
- 只测自己写的,不测框架的
- service 中的每个方法
- helper 中的每个方法
避免
- 构造方法中做的事情过多。
- 存在过多的全局变量和静态方法。
- 存在过多的外部依赖。
- 存在过多的条件语句。
- 私有方法
- controller, job, command 中的方法
单元测试代码规范
- 必须放在 tests 目录下
- 目录划分同app目录一一对应
- app目录下有一个类,那么test中有一个对应的Test类,文件名后缀为Test
- 每个Test 的方法必须加前缀test,必须为public
单元测试具体实践
helper测试
不涉及数据库不涉及API,保证断言能够覆盖到所有的函数分支即可
DB测试
DB测试是比较难的,DB中的数据其实都是全局变量,违背了原则不应存在过的全局变量
。
DB测试类启用事务,保证代码的可重复执行。
DB新增数据,为保证可重复执行,应启用faker和factory造数据。
DB修改、删除、查询,可能需要依赖新增数据,启用 @depends 指定依赖关系。
DB测试具体参考乐享:
faker参考乐享:
cache测试
phpunit.xml 默认设置的即array
<env name="CACHE_DRIVER" value="array"/>
API接口测试
mock接口测试
单元测试目标 - 代码覆盖率
期望设定目标 service和helper中的方法覆盖率达到 100%。
- 行覆盖率(Line Coverage)
行覆盖率(Line Coverage)按单个可执行行是否已执行到进行计量。
-
函数与方法覆盖率(Function and Method Coverage)
函数与方法覆盖率(Function and Method Coverage)按单个函数或方法是否已调用到进行计量。仅当函数或方法的所有可执行行全部已覆盖时 PHP_CodeCoverage 才将其视为已覆盖。 - 类与特质覆盖率(Class and Trait Coverage)
-
类与特质覆盖率(Class and Trait Coverage)
按单个类或特质的所有方法是否全部已覆盖进行计量。仅当一个类或性状的所有方法全部已覆盖时 PHP_CodeCoverage 才将其视为已覆盖。 -
Opcode 覆盖率(Opcode Coverage)
Opcode 覆盖率按函数或方法对应的每条 opcode 在运行测试套件时是否执行到进行计量。一行(PHP的)代码通常会编译得到多条 opcode。进行行覆盖率计量时,只要其中任何一条 opcode 被执行就视为此行已覆盖。 -
分支覆盖率(Branch Coverage)
分支覆盖率(Branch Coverage)按控制结构的分支进行计量。测试套件运行时每个控制结构的布尔表达式求值为 true 和 false 各自计为一个分支。 -
路径覆盖率(Path Coverage)
路径覆盖率(Path Coverage)按测试套件运行时函数或者方法内部所经历的执行路径进行计量。一个执行路径指的是从进入函数或方法一直到离开的过程中经过各个分支的特定序列。 -
变更风险反模式(CRAP)指数(Change Risk Anti-Patterns (CRAP) Index)
变更风险反模式(CRAP)指数(Change Risk Anti-Patterns (CRAP) Index)是基于代码单元的圈复杂度(cyclomatic complexity)与代码覆盖率计算得出的。不太复杂并具有恰当测试覆盖率的代码将得出较低的CRAP指数。可以通过编写测试或重构代码来降低其复杂性的方式来降低CRAP指数。
FAQ
- @depends 被多个函数依赖,只会执行一次,如删除、修改均依赖创建,如果delete先执行,再执行update就会有问题
目前没有好的解决方法,就是通过调整执行顺序来解决此问题
- 单元测试执行时间长
待解决
- 抛出异常的代码如果正常进行单元测试