单元测试简单来说就是开发人员对自己所编写的代码进行测试。
有的会说:还要写代码啊,我哪有时间啊,业务代码我都写不完,还写测试代码啊。
但是,静下来想一想我们的项目周期一般来说时间是比较充裕的,周期一般来说比较长。
有的人可能会说:我从事开发都好几年了,都没有做过单元测试,不一样过来了吗?所以,觉得不做单元测试,也可以活得很好,很开心啊。几年的开发经验,编码功力在,对自己的代码还是很有信心的,单元测试这种简单的玩意儿完全不必要。
但是,我们都知道在 Android 开发领域有一个叫 Jake Wharton 的人,他是 OkHttp,Retrofit,RxJava 等多个著名开源库的主要开发者,曾经入职 Google,据说推动了 Jetpack 这套组件的出现。这样一个人的开发年限想必要比多数人要长,开发经验以及编码功力要比多数人深厚,这点应该是没有异议的。那么,这样一个大佬想必最有资格不做单元测试了。
我们打开 Retrofit 这个开源库,可以看到大佬认真做的单元测试:
有的人可能会想:Android 都已经发展十几年了,想必会有一种"自动化"的测试,会”自动“地去完成我的每一行代码的测试,而我不用去做任何单元测试的代码编写工作。
假如存在这么一种"自动化"的测试,那么 Google 为什么没有推荐给广大的开发者呢?为什么 Android Studio 默认工程模板里面还要去保留 androidTest 和 test 这两个目录呢?根本就不存在的。如果还是心存侥幸,不太甘心,可以打开 Google 的官方文档网站,里面有一个讲解测试的部分(实际上,如果打算看下来这个测试部分的文档,完全没有必要继续阅读本文了),可以看看有没有说这种"自动化"的单元测试。
到这里,小结一下:单元测试并没有过时,还是需要的。
说一下单元测试的好处,这样大家在单元测试的路上就更有目标,更有劲头了。
单单看这一点就足够激动人心了。作为开发人员,哪个不希望自己的 bug 少一点?哪个不希望别人说自己的代码 bug 少?作为公司,交付稳定的代码,自然会增加客户满意度,培养出忠诚的客户。
自己维护的老项目,大多数情况下是没有新需求的,维护的内容主要就是修改客户反馈的问题,有一段时间平均每天都有至少 5 个问题轰炸过来。有些问题,客户有图有日志,确实就是编码上有问题。假如有单元测试的加持与保证,相信有部分 bug,在开发阶段就已经被干掉了,是不会让客户有机会发现的。
不敢奢望自己的代码一点问题都没有,也不去比较谁的代码 bug 多或者少,通过单元测试哪怕可以降低一点点 bug,那么这都是很有意义的一件事情。实际上,随着单元测试覆盖范围的增加,bug 出现的概率也会随之减少的。
单元测试对某个函数的各种输入输出都进行了测试;如果在重构的时候,改变了这个函数的实现,只需要重新跑一下这个函数的单元测试,就可以确定重构有没有带来问题。
需要说明的是,有的重构可能会破坏掉之前写的单元测试,这时就需要重新编写新的单元测试了。但是,如果重构破坏了很多单元测试的话,可能整个代码架构设计就是有问题的了。
在针对一个函数或类写好单元测试代码之后,可以随时随地地快速运行测试。也就是说,单元测试作为一种代码的形式,可以使用很多次。
看到这点,可能会说单元测试代码只是用来测试业务代码而已,怎么可能优化业务代码呢?这一定是在吹牛,是在放卫星。
真的不是的。
一个我们不愿意面对却是不争的事实是,并不是什么代码都可以直接拿来单元测试的,或者说,只有那些职责清晰,隔离明确的代码才可以很好地进行单元测试。
也就是说,如果要方便顺畅地编写单元测试,首先就要把代码重构成那种好的代码。什么是好的代码呢?满足设计模式七大原则,这点可以参考 Google 的 App Architecture。
这里并不是说我们写的代码就不好,只是按照我们的思维习惯,确实容易写出耦合的代码。只要参考优秀的架构,重新跟着改造一下,就更好了。
看到这里,一定想着开始进行单元测试了。但是,不禁会想单元测试怎么做呢?我该怎么开始进行单元测试呢?
不用担心,在单元测试的路上,已经有太多的前辈们走过了。我们并不需要筚路蓝缕,以启山林。
这些东西的学习略显枯燥,但是不要忘记了 ”磨刀不误砍柴工“。
参考博客:
https://blog.csdn.net/qq_17766199/category_9270906.html?spm=1001.2014.3001.5482
这些文章写得比较早了,但是不少内容并不过时,可以参考学习一下。
各个框架的官网非常值得浏览,这上面的东西一定是最新的,同时也会有一些教程:
Hamcrest:http://hamcrest.org/JavaHamcrest/tutorial
mockk:https://mockk.io/
Robolectric:https://robolectric.org/
官网测试文档地址:https://developer.android.google.cn/training/testing。官方文档永远都是我们的指明灯,Google 会收集并吸收全世界优秀的开发思想,架构,并体现在文档里面,所以时常来看看这里的更新是很有好处的。
官网上的测试部分有两个 codelab,提供了手把手的单元测试教学,具有很强的实操性,非常具有学习价值:
https://developers.google.cn/codelabs/advanced-android-kotlin-training-testing-basics#0
https://developers.google.cn/codelabs/advanced-android-kotlin-training-testing-test-doubles#0
从两个 codelab 里面学习到一些东西,这里也写一下:
把 iovue 的数据源封装在接口之下,也就是说,从 hmi 角度看 iovue,iovue 不过是检测,下载,安装,查询这几个接口的实现而已,hmi 层不再关心 iovue 内部是如何实现的,只关心这些接口会得到哪些情形的数据即可。这样,我们完全可以构建出一个实现了这些接口的假的 iovue 了,它就可以向 hmi 返回与真正的 iovue 一样的数据了,极大地方便了单元测试。其实,这就是应用了设计模式的依赖倒置原则(dependence inversion principle),也叫面向接口编程,通俗来说,就是增加了一个隔离层而已。
codelab 提供了一些测试的最佳实现,例如如何使用 Android Studio 生成测试类,如何在 Android Studio 里面查看测试结果,如何测试获得 LiveData 的可观察数据,如何命名一个测试,如何设计自己的测试(Given,When,Then 三大步),如何测试使用了协程的代码(如果是使用了 RxJava,可以参考 https://weilu.blog.csdn.net/article/details/78989664)。
这点觉得有点多余,但是还是啰嗦啰嗦。
单元测试是要写代码的,刚开始写单元测试可能会很累,想放弃,”万事开头难“,请坚持下去,坚持把从博客,官网,codelab 里面学习到的东西尽可能地应用起来;就这样一周或者两周后,就可以感受到写单元测试的快乐了。