最近两周一直跟公司里面的一些工程师讨论单元测试的价值,是不是只要有端到端的自动测试已经足够。很显然我的观点是都要有,而且要以单元测试为主,为基础。
持反对意见的工程师觉得因为我们已经有了从页面到数据库的端到端的测试,能够覆盖到系统中各个层次,因此就没有必要再去为每一层中的各个类去写单独的单元测试。我认为可以从几方面来考虑这个问题。
- 复杂度上不允许
很显然为单独一个类写测试,只需要考虑有限的几个情况(当然如果是那种想XXXManager一样的怪兽类,那就是另当别论了),因此测试会比较简单。如果采取端到端的测试,从页面层到数据库至少要涉及十个类。如果采用端到端的测试,每个类方法假设有1个IF语句,也就是有2种情况,为了做到逻辑全覆盖,我们需要测试到2的10次方(1024)种情况。如果我们使用单元测试,首先我们确保每个类的逻辑覆盖,每个类需要2个测试,一共用20个单元测试,然后为了保证能够贯通,我只要在采样几个集成测试(或称为打穿测试)就可以了。因此获得同样的结果,我测试问题复杂度要小得多。如果遇到那种很大的类方法(圈复杂度很高),这更是不可能的任务。
- 时间不允许
另外集成测试的运行时间比较长,一般需要几分钟才能运行完一个用例,因此如果需要运行成千上万个,那反馈也会很不及时。如果反馈不及时,那把测试放到持续集成里面就没有意义了。如果不放到持续集成中经常运行的话,测试就会过时,发臭,最后被抛弃。
- 经济上不允许
一般花很短的时间就可以完成一个单元测试,而完成一个端到端的集成测试则需要花几倍的时间。我见过有测试团队花了几个月的时间完成了几百个测试。仅仅为了一个简单的逻辑,为了逻辑覆盖,就需要写上千个测试,这明显从经济角度也是不可能的。
- 问题难以定位
系统一旦出现问题,由于涉及的类太多,很难一下子确定问题出现的地方。还是需要去Debug。
因此单元测试对保证系统的内部质量十分关键,但是集成测试,功能测试以及基于页面的测试也是十分必要的。针对不同的测试类型,以及它们之间的关系,Mike Cohn提出了一个测试金字塔
- 最上层的测试主要是作为打穿测试,测试系统的各个部分,各个架构层次是否能够无缝集成。
- 中间层次是以Fit测试为代表的面向业务的测试,主要测试系统的业务逻辑是否正确,是否在做正确的事情(Do Right Things)。
- 最下面是开发人员写的测试,主要是控制实现层面,也就是是否正确地做事情(Do Things Right)。
道理其实跟装配汽车一样。任何公司都不会把汽车完全装配起来再进行测试。通常的做法是先对零件做充分的测试;然后把零件装配成部件,比如车门,发动机,车轮等等,接下来针对部件进行全面的功能测试,测试这些零件组合起来是否能够满足需求;最终才会把汽车装配起来进行整车测试。归根结底我们要兼顾各种测试类型,夯实作为针对软件零件的基础性单元测试,确保每个软件单元都能正常的工作;在更高一个层次尽量使测试覆盖所有业务逻辑;在最上层通过一定数量的基于页面的端到端测试主要保证各部分组件能够顺利集成。