去年换了一个新部门,看了下当前的自动化用例的情况,发现存在三类性能问题:
对于上述三个问题花时间进行了一定程度的优化,总结如下
通过调试可以发现,一个需要执行660ms的case,在执行前的初始化工作就需要消耗约1分半钟,那么就需要思考下能否减少这部分初始化时间了。
公司用的自动化框架是基于AbstractTestNGSpringContextTests的框架。AbstractTestNGSpringContextTests是一个spring集成testNg的工具,可以通过ApplicationContext加载bean。ApplicationContext实现的默认行为就是在启动服务器时将所有bean提前进行实例化。提前实例化意味着作为初始化过程的一部分,applicationContext实例会创建并配置所有的bean。
如果是作为一个spring服务,在启动时将bean提前进行实例化,然后可以处理所有的请求,这样的做法是很合理的。但是作为本地调试,更关注是自己case运行时所需要的bean是否实例化,而不需要将所有bean进行实例化。
查阅了下spring相关文档,发现可以引入lazy-init来告诉ApplicationContext按需加载bean。
配置方式有两种:
default-lazy-init参数,其配置形式如下:
lazy-init参数,其配置形式如下:
配置完成后,运行了一下,发现并没有速度上的提升,原因是之前编写时将大部分的bean的初始化放在了测试用例里的基类里面,导致启动时认为这些bean都需要初始化。
这说明要让lazy-init生效,提高单用例的启动速度,那就要尽可能少的使用不需要的bean,需要做一定改造:
按照上面的思路对用例进行了优化,可以将用例的初始化时间精简到30秒左右。
为什么会出现很多的用例执行时间超过2分钟呢?做了一些分析和调试后,主要有几个原因:
下面来看几个案例
在beforeclass里面都会有一段初始化数据的操作,先调接口查询数据是否存在,不存在则进行初始化,导致每运行一个测试用例类都需要做一次对应操作。
实际上这些数据初始化完后可以一直被使用,不需要多次检查,可以优化的地方是用个静态变量判断数据已初始化的话就不检查,或者将该操作设置为跑一次用例集只运行一次。
大量使用了sleep做等待,如果操作需要等待1s左右才生效,那么用sleep往往需要sleep2秒,所以sleep一般会造成50%左右的性能浪费。
引入异步校验工具Awaitility对原有代码进行改写。
Awaitility的基本的语法为:
这样的写法比较优雅简介,如果判断执行完成可以提前结束等待,避免时间浪费。
当优化了单用例的运行时间后,虽然对总体自动化集成测试的运行速度有一定帮助,但当用例越来越多的时候,时间也会变得无法忍受,能想到的一个办法是增加用例的并发。
用例能够并发执行的前提是用例之间具有隔离性,一个用例的执行不会影响另一个用例的执行,比如我在店铺A下单和在店铺B下单这两个用例就不会有干扰,又比如我在店铺A创建商品和我在店铺A下单也不会有影响。
所以考虑用例并发的时候,需要先针对自己的业务特性进行一定程度的分组隔离。
在我们的案例中,考虑对店铺进行分组,用例并发用到的并发基本机制是testNG parallel="tests"/"class" thread-count=“N"。
在实际执行中,分组的实施也会有两种模式,按case的纬度还是按照类的纬度:
1.使用店铺id分组进行并发,使用group=店铺id 维度
优点:任意维度扩展
缺点:每个case需要加@group
2.把不同测试类按店铺id分组,使用package/class维度
优点:改动简单
缺点:需要每个测试类只使用一个店铺id,缺乏扩展性,需要频繁改动配置文件
最后选择了按case纬度,因为现存的用例并未很好的按店铺id进行组织,比较散乱,使用类的纬度改动较大。
使用了两个并发以后,性能提升明显,时间从547s->270s。
解决了一部分性能问题后,尤其是提高了用例并发以后,对用例稳定性也更高了。
和开发写代码需要考虑异常和容错处理一样,测试人员在自动化设计、实施等各阶段都需要考虑用例的稳定性问题:
感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取