提到自动化的测试策略,大家很容易想到的就是测试金字塔,的确作为一种常见的自动化策略,测试金字塔确实能给我们带来很多思路。但是具体的项目的实际情况会不一样,比如很简单的一个例子,如果项目是前后端分离开发,我们可能要考虑用契约测试,但是如果是没有分前后端,我们更多的考虑的是做更多的集成测试。
今天我就以之前的一个项目为例子,来说一下我们怎样更好的运用金字塔。整体的项目结构其实很简单,最上面是react native写的IOS和安卓的客户端,接着是BFF,再往下是微服务供给BFF调用。项目不分前后端,卡的完成标准是从前端到后端全部完成。所以项目初期我们我把契约测试排除在外,打算做更多的集成测试。
金字塔的具体结构
整体结构如下,包含E2E测试,集成测试和单元测试。细分的话,集成测试又包括,web端的集成测试,BFF的集成测试,微服务本身的API测试,每次提交代码,我们都会把微服务相关的其他服务启起来,方便去做集成测试。很多人可能会问,那集成测试难免会有重复,但我认为有一定的重复是好事,只要控制好“度”就可以了,这个我在稍后会给大家详细的介绍。
关于E2E测试
因为基于项目本身的技术栈,对于IOS和安卓的客户端,我们采用了Appium,但是CI的server上是不可能装IOS和安卓的模拟器的,所以我们选择了用sourcelab作为运行端到端自动化的平台。
我们不打算一上来就开始写E2E的测试,一是由于刚开始页面不稳定,有可能会有改动,维护的成本太大;另外一方面对于端到端的测试,我们本来就只打算cover有business value 的部分,对于页面的UI 的display的验证,我们考虑用别的方法去做,稍后会详细说明。所以基本上到了项目的中期我们才开始写E2E的测试,而且写的量很少。
那在端到端测试这么少的情况下,我们如何保证质量呢?如果去验证UI的显示呢?看完文章了自然会有答案。
关于集成测试
上面提到了对于集成测试,我们有Web端的集成测试,BFF的集成测试,API的集成测试。为什么要分这么细呢? 其实各个层的侧重点是不一样的,Web端的集成测试侧重点在于整体的集成;BFF的侧重点在于BFF和后端微服务的集成;而API你可以直接理解为微服务的接口测试。
其实这三者也是一个小金字塔的关系: 整体看来三者数量的比例是:API集成测试>BFF的集成测试>Web的集成测试。举一个很简单的例子,对于API测试来说,我们可能会覆盖很多正向和负向的cases,但是对于web端来说,我们并不会像做底层API一样做的那么详细,最主要的cases去覆盖了就可以。
之前上面也提到了,这可能会导致重复浪费的现象,比如一个API可能在API集成测试的时候做了,然后在BFF里面也可能也会做,最后在web端的集成测试还会做一次。 确实,多多少少会有些浪费,但是我们在项目中尽量会把浪费控制的比较低,我们有一个很详细表格,里面包含每个卡的ACs,会对应到某一层做了哪些测试,以此来减少浪费。 比如我们发现这个AC在web的集成测试里面做了,那么我们就不会在BFF和API的级别再重复做一次。 但是有时候测试有一些交集也是好事,但是前提是要控制好度。
关于单元测试
从上面的图也可以看出来,对于mobile,BFF和微服务,我们都会写单元测试。单元测试里面,我们还会去做页面UI的验证,这也回答了之前在E2E测试里面提到的问题,那具体怎么去做的呢?
首先对于react native的一些组件的功能测试,我们做了如下测试,我们会传一些mock的数据直接给react native的组件,去验证组件的显示是否正常。如下图,我们会去验证组件上的控件的显示结果是不是我们需要的。
其次是用snapshot测试保证UI的显示,我们会去比较整个页面的html元素,如果发现和前一次的不一样,就会直接报错。但是我个人认为snapshot在后期可能作用会大一些,因为前期页面变动太大,会需要频繁的改动。 虽然维护的成本不大,但是也是一种浪费。
再次,上面提到了验证页面的组件是否能够显示正常,也提到了验证UI的显示,那如何验证页面的功能呢? 比如我点了一个button之后,如果确保这个button触发了我想要的功能呢? 如下图所示,我们会在单元测试里面模拟一些UI的操作,比如,点击一些button,验证是否调用了相应的方法,并且传了我们所需要的参数。 至于方法是不是写的正确,不在单元测试的验证之中。上面提到了我们已经有集成测试去覆盖,这样就能很好的进行解耦。
当然在单元测试中,我们还包含了一些其他的测试,比如对Store的测试,我们本地会维护一个store,比如在save一个一个record之后,本地也会相应的存一份。 如下图所示,在update某条数据之后,我们会在store里面把数据拿出来与我们期望的进行比较:
在整个测试过程中,我们会把每个card的AC单独的维护在一个表里,然后原则是尽量使得每个AC都能在自动化中进行覆盖,而且是尽量的把测试下移。这样我们在做regression或者手动测试的时候也有更多的时间去做探索性测试。
其实在具体的项目中,用到的策略可能会有所差异,比如在某些时候还要考虑是否需要引入契约测试,mock服务等等。 但是整体的原则我们认为是大同小异的。前提需要我们能对项目的技术栈,工作模式,依赖等有很好的认知。