如何写单元测试

可能大家会觉得,单元测试有什么好讲的,那请大家思考以下问题:

  1. 你有没有写过单元测试
  2. 你写过多少单元测试,是否感到厌烦
  3. 当提到单元测试,你的第一反应是什么?只是写一段代码来测试代码?有没有一套完整的原则去遵循?
  4. 当你写了一段代码来测试,是否只是测试了正确输入,得到正确结果?
  5. 你的单元测试运行过多少次? 是否只是第一次运行了就再也没管过,尽管后来代码改了?

如果上面有哪一条满足了,我觉得就有必要研究下单元测试。

单元测试是什么

必须先形成一种思想:是发自内心的,而不是被强迫去做,成为一种负担。不要为了写单元测试而写单元测试!

谨记:单元测试不是负担,而是一种资产,能够为我们带来价值和便利。

价值:

  1. 深入理解需求
  2. 覆盖边界条件,减少bug
  3. 工作量证明,有据可寻,出现问题易于排查,心理上的安慰

便利:

  1. 修改代码后可以快速判断对不对(infinitetest
  2. 从测试理解函数的作用,对别人有帮助

缺点:

  1. 工作量变大了:体现在2个方面
    1. 写的多了
    2. 改得多了,如果原来的业务变了,则该删的和该修改的代码也多了
  2. 对程序员的要求高了:如果是TDD,则需要先写测试,而很多人都是在写完业务才理解业务逻辑的
  3. 单元测试不能应对所有情况,有可能单个方法是对的,但集成在一起就出问题了

总的来说:利大于弊,单元测试能让未来更加美好!

单元测试的粒度

面向过程和面向对象以模块和类区分文件,但单元测试的粒度建议以方法(函数)来写。

理论上:根据单一性原则,一个类只完成一个业务,一个方法只完成一个功能,但现实是很残酷的,不是所有人都这么做,也不是所有功能的界限都那么明确。

比如登录功能:写在一个方法,但其实可以划分得更细:参数检测,用户名密码查询,返回结果。

我们采用一个方法一个单元测试:但一个单元测试并不是只有一个方法:

如果一个方法很复杂,参数太多,一个测试方法可能很长,可以适当地拆分,但只有一个方法有@Test注解:

1

这样的话,拆分出的方法为了满足开闭原则,应该是private的,这给测试带来了了困扰。
//TODO
试试看反射是否满足要求

单元测试原则

每次谈原则之类地东西都觉得是虚的,太过于理论,我开始也这么觉得,但后来逐渐明白,原则,或者说原理是经过高度总结概括和抽象地,
如果看不懂原则,那说明你还处在社会底层,不能理解上流社会的简洁与美丽。
我可以举个简单的例子来说明这个过程如何达到:小时候我们肯定听过一句话:书读百遍,其意自现。

当然,背诵不是唯一的途径,主要是当提到单元测试时,内心第一反应就是这些原则,我们所坚信的东西。可以在每次写测试前后对代码进行原则比较,
看是否满足。

FIRST

  • Fast:除去被测方法本身,单元测试不应该过于复杂,保证速度快,如果方法本身很慢就没办法了
  • Isolated:隔离性,单元测试间不应该相互依赖,且没有先后顺序
  • Repeatable:可重复执行,而不是随时间变化而不可预测
  • Self-validating:可以自我验证结果,不需要人工干预,也就是使用Assert,而不是打印出来判断
  • Timely:及时性,你肯定不会等代码上线了才来补测试把,能尽快就尽快

AIR

阿里的空气原则,来自Java开发手册:

  • Automatic:自动化,类似Self-validating
  • Independent:独立性,等于隔离性
  • Repeatable:可重复性

测试的最佳实践

BCDE原则

阿里手册里提到的关于测试的范围的原则:

  • B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
  • C:Correct,正确的输入,并得到预期的结果。
  • D:Design,与设计文档相结合,来编写单元测试。
  • E:Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得
    到预期的结果。

命名清晰

关于测试方法的命名不需要节省字符,尽量把做什么和期待什么结果写出来,这样注释就可以省了:比如:

1
2
3
testCreateUserCorrect()
testCreateUserNullShouldThrowException()
testCreateUserDuplicatedShouldFail()

测试代码与业务代码分离

对异常的捕获

1
@Test(expected=SomeDomainSpecificException.SubException.class)

别依赖于你的机器

单元测试里不应该包含只有你的电脑上才有的东西,比如一个PATH路径,一个文件之类的。

DAO层如何测试

Service层如何测试

使用spring Mock

我们的测试规范

Junit

1
2
@BeforeClass
@AfterClass

java和scala如何写单元测试