You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
15 KiB
Markdown

2 years ago
# 04脚本复用什么样的代码才值得写
你好,我是柳胜。
开发和测试团队我都带过现在测试人员的代码能力越来越强了已经接近开发人员我看过一些测试牛人设计的测试模块和代码MVC实现测试控制台、分布式多测试节点管理、Proxy对测试Interface默认实现……这些用到的技术栈和开发不相上下。
聊下来,很多的反馈是“能做起来,很庆幸有一个对测试技术很支持的领导”。这就是一个让人困惑的问题,为了开发自动化测试案例,你写了那么多代码,那每一行代码就应该存在它的价值,这个价值是客观的,而不是依赖主观的某个人的认知。不是么?
所以这一讲要关注的问题是你写的每一行代码都有自动化测试的价值么你能把它说出来说清楚么想清楚这些你自然也会明白给定一个自动化测试的项目哪些工作是overwork过度工作哪些是underwork工作得还不够
## 哪些代码值得写?
在开始之前我们再回顾一下自动化测试ROI模型。
![图片](https://static001.geekbang.org/resource/image/cd/00/cd34280bc70b3633e696a7ba16f9e300.jpg?wh=1920x868)
一个案例转化成自动化测试后我们的目标是它的投资回报率越高越好在ROI公式里回报也就是分子越高越好成本也就是分母越低越好。
在第一讲我讲到过n是自动化测试案例运行的次数在回归测试里n是回归迭代的次数回归次数越高n也就越大这是从时间的角度上来看n。在这一讲我们换个角度从空间来看也就是代码的复用率我们有没有办法让代码的复用率升高
### 初始版本
为了让你更容易理解,我们结合一段登录的脚本,一起探索一下怎样提高一个自动化测试案例的复用率。
脚本代码如下:
```java
@Test
public void login() {
WebDriver driver=new ChromeDriver();
driver.manage().window().maximize();
//打开页面
driver.get("https://www.example.com/users/sign_in");
WebElement username=driver.findElement(By.id("user_name"));
WebElement password=driver.findElement(By.id("user_password"));
WebElement login=driver.findElement(By.text("登录"));
//输入用户名
username.sendKeys("liusheng@example.com");
//输入密码
password.sendKeys("123456");
//点击登录按钮
login.click();
}
```
这段脚本实现的功能很简单启动chrome浏览器打开一个登录链接在页面上输入用户名liushing@example.com密码123456点击“登录”按钮完成登录。此处我省去了assert检查点。
现在这段脚本每运行一次就测试一次登录。它的n现在就等于1。我们想一下这个脚本还能怎么提高它的复用率呢
### 提高复用率:一份代码,多浏览器运行
可以看到脚本运行的测试案例只在chrome上但作为一个web应用一般是要支持市面上主流的浏览器看一下[阿里云网站](https://help.aliyun.com/document_detail/211434.html)支持12种浏览器列表如下
![图片](https://static001.geekbang.org/resource/image/32/5a/32647ecb58ae59f29f1e6aec994f6f5a.jpg?wh=1920x1130)
那么有没有办法让我们的脚本能够一下子测试12种浏览器呢此时我们需要修改脚本支持调用多个浏览器driver
```java
@Test
@Iteration(Driver=ChromeDriver,FireFoxDriver.....)
public void login() {
//此处是伪代码代表从Iteration数组里拿到的driver元素
WebDriver driver=new Iteration("driver");
driver.manage().window().maximize();
//打开页面
driver.get("https://www.example.com/users/sign_in");
WebElement username=driver.findElement(By.id("user_name"));
WebElement password=driver.findElement(By.id("user_password"));
WebElement login=driver.findElement(By.text("登录"));
//输入用户名
username.sendKeys("liusheng@example.com");
//输入密码
password.sendKeys("123456");
//点击登录按钮
login.click();
}
```
上面是伪代码代表Test会重复运行Iteration数组driver的数目有多少个就运行多少次每次运行从iteration数组里取得driver的名字交给脚本去启动相应的浏览器。
现在dirver有12个我们就可以让一份脚本测试12个浏览器获得了n1=12。
总结一下,最佳实践:一份代码,兼容多个浏览器。
### 提高复用率:一份代码,多数据运行
刚才已经迈出了第一步不错我们再继续看脚本还有没有可以改进的地方。现在我们的脚本只能测试一组用户数据用户名liusheng密码是123456。在测试方法论中一个测试案例应该有多组测试数据那合法的用户名的数据格式不止这么多按照字符类型划分等价类至少有5组
1.ASCII字符
2.数字
3.特殊字符
4.拉丁文字符
5.中文字符
你如果有兴趣想知道为什么这5种字符是等价类可以去研究一下字符集原理。
密码一般是数字ASCII字符加特殊字符3种。我们至少可以开发出5\*3=15种合法的用户名密码组合作为测试用例。
```java
@Test
@Iteration(UserPassword={xxxx,123456},{测试用户Welcome1}....)
@Iteration(Driver=ChromeDriver,FireFoxDriver.....)
public void login() {
//此处是伪代码代表从Iteration数组里拿到的driver元素
WebDriver driver=new Iteration("driver");
driver.manage().window().maximize();
//打开页面
driver.get("https://www.example.com/users/sign_in");
WebElement username=driver.findElement(By.id("user_name"));
WebElement password=driver.findElement(By.id("user_password"));
WebElement login=driver.findElement(By.text("登录"));
//输入用户名
username.sendKeys(UserPassword[0]);
//输入密码
password.sendKeys(UserPassword[1]);
//点击登录按钮
login.click();
}
```
上面的代码是伪代码UserPassword数组有多少个元素测试案例就会运行多少次。每次运行会从interation里取得数组的一个元素一个元素就是一种用户名密码组合。
上面的UserPassword数组有15个元素我们的测试案例就运行15次现在n2=15加上浏览器的12次n=n1+n2=15+12=27次。
最佳实践:一份代码,多组测试数据。
### 提高复用率:一份代码,多环境运行
现在我们已经摸着道了提高ROI那就是让一份自动化测试程序尽可能多复用在不同的测试场景中。这些测试场景本来就是有效的测试需求转换成自动化也是一劳多得。
还有没有其他场景呢当然有举个例子在我们的产品发布pipeline里贯穿了从开发环境、测试环境、准生产环境到生产环境由低向高的交付过程。
那你的测试脚本需要兼容每一个环境在所有需要运行它的环境里都可以直接跑不需要做任何修改。一份脚本运行在devteststageproductin 4种环境下我们的n3=4, n=n1+n2+n3=15+12+4=31次
你可能会说这个有难度呀环境不同不光是脚本url不同里面的测试数据也不一样测试配置也不一样甚至timeout要求也不一样等等不是那么容易实现的。那你可以想想这种问题是不是开发也会遇到他们需要把服务部署运行在不同的环境下。开发是怎么做到的
以SpringBoot为例它提供了多profiles配置的功能application.yml文件配置默认参数application-dev.yml里放dev环境的配置参数application-test.yml放test环境的配置参数application-prod.yml里放production环境的配置参数。
当spring boot application启动时会自动根据输入环境参数加载相应的环境yml配置文件。这就实现了一份代码多环境部署的场景。
我们的自动化测试也可以实现类似的机制。聪明的你可以考虑自开发一个测试配置文件加载模块代码不需要多但会直接增加ROI。
最佳实践:一份代码,兼容多环境运行
### 提高复用率:一份代码,多语言运行
另外,如果你的产品支持多国语言,那么**一份代码跑多国语言版本**也是一个会显著增加自动化测试ROI的好主意。
像上面的代码当前只支持中文页面。假设我们的产品要求支持9种语言那可以让页面控件加载不同语言的label text。
伪代码如下:
```java
@Test
@Iteration(UserPassword={xxxx,123456},{测试用户Welcome1}....)
@Iteration(Driver=ChromeDriver,FireFoxDriver.....)
@Iteration(Profiles=auto-dev.yml,auto-test.yml,auto-prod.yml....)
@Iteration(Language=en,zh_CN,zh_TW, FR....)
public void login() {
//此处是伪代码代表从Iteration数组里拿到的driver元素
WebDriver driver=new Iteration("driver");
driver.manage().window().maximize();
//打开页面
driver.get(profile.getUrl());
WebElement username=driver.findElement(By.id("user_name"));
WebElement password=driver.findElement(By.id("user_password"));
WebElement login=driver.findElement(By.text(Label.getLoginText(language)));
//输入用户名
username.sendKeys(UserPassword[0]);
//输入密码
password.sendKeys(UserPassword[1]);
//点击登录按钮
login.click();
}
```
现在一份脚本经过了多浏览器、多数据、多环境和多语言4轮打磨运行的次数n=n1+n2+n3+n4=12+15+4+9=40次。如果各个场景有关联关系比如页面的语言和测试数据有耦合英文页面的encoding和数据的charset有关联那么两个场景的次数就是完全组合采用乘法15\*9=135次。
而且从脚本的变化可以看到脚本第一版本里的hard code也一个个被消除了取而代之的是数据驱动。**消除hard code是提升ROI的结果**。
当然上面的代码都是伪代码实际上为了支持多场景运行付出的努力不止是增加循环那么简单。比如支持多少种浏览器取决于框架而不是脚本。像现在有一些新的基于JavaScript的测试框架只支持chrome浏览器开发人员很喜欢用它来验证功能但从自动化测试角度来看它的ROI就受限了这些局限都应该框架选型时考虑进去。
## 还有哪些工作值得做?
第一讲提出ROI模型的时候我就提过成本里的维护工作量是一个不确定的风险。根据ROI金字塔模型维护的工作量也是自底向上增加不确定性增加。
![图片](https://static001.geekbang.org/resource/image/07/f6/07aaf08df0b9a44987ed9a38faa412f6.jpg?wh=1920x1228)
维护工作量的不确定性是自动化测试的一个重要风险,所以我们有必要看一下维护的工作量都花在哪里了。
1.被测截面发生变化带来的维护工作量。比如UI自动化测试的产品页面发生了变化API自动化测试的接口做了重构。
2.诊断自动化测试的工作量,如果把自动化测试结果分为真阳,假阳,真阴,假阴。那假阳和假阴都是需要诊断的。
诊断是要花费时间的,有这么几块:
1.从错误实际发生,到我们知道错误发生,这有一个**通知**的时间;
2.从开始诊断错误,到定位出错误,这要花费一个**诊断**的时间;
3.修复错误和验证修复方案,这也要时间,即**修复**时间;
4.修复上线后,跑出第一轮测试结果,证明完全恢复,这叫**确认**时间。
![图片](https://static001.geekbang.org/resource/image/e8/f4/e85a5b985324f48fdbeef5de0d9178f4.jpg?wh=1920x459)
怎样能提高诊断的速度呢?从上面的分析,可以看到:
1.缩小通知时间,目标做到**实时**通知。一旦有错误出现,应该立刻有责任人被通知到,进入到诊断环节。
2.诊断越快越好,一旦确定为自动化测试脚本的问题,应该立即对团队做出说明,并将错误案例下线,避免持续出现相同错误,引起误解。
3.修复要彻底,一个案例持续不能给人信任的结果,将会打击团队的信心。我见过有的公司,自动化测试跑完一遍后,还要手工再去验证一遍,这样的自动化测试就失去了价值。
4.确认后,自动化测试快速恢复上线,开始使用。
你在这里,可以发挥技术能力,按照上面的方向努力,去降低自动化测试的维护成本,比如:
1.Log规范+ELK+Grafana实现告警实时传达。
2.检查点+日志+屏幕截图甚至视频,提高诊断效率。
3.高内聚低耦合的模块化设计,能够实现隔离错误,缩小影响范围,快速修复的效果。
4.自动化测试上下线的标准和流程的建立。
![图片](https://static001.geekbang.org/resource/image/3f/26/3ffdb78yy842c96f530158b22e06d026.jpg?wh=1920x591)
## 小结
今天我最想和你传达的观点是:**在提高收益的方向上,我们付出的每一份努力和尝试,都是值得的。**
其实自动化测试有个特点,它并没有一个显式的可验收交付目标。我们既可以写几行脚本就自动化一个案例,也可以实现一个支持控制台、多代理、持久化等功能的复杂系统。
但是别忘了自动化测试的本质它是一个有业务需求的软件实现。这个业务需求以前没有人去讲清楚学过这一讲你就应该明白了业务需求就是自动化测试ROI想办法提高它是这个软件的唯一目的。
我讲到了提高代码ROI的通常的四种思路一份代码多浏览器运行多数据运行多环境运行多语言运行并用例子向你展示了这个提供ROI的过程也是消除代码Hard code优化代码结构的过程。
另外降低维护成本也是提高ROI的一个非常有效的办法。我们分解了自动化测试的诊断时间分为通知、定位、修复和确认四块时间针对每一块时间都有相应的办法去提高效率这里我列出了一些常见办法帮你在工作中找到更精准的目标你可以保存下来做个参考。
总之任何能够提高ROI的代码都是有价值的反之就是overwork。作为自动化测试架构师的你在头脑里应该存在一个优先级从高到低的工作列表先做哪块再做哪块按照ROI从高到低的顺序来安排。
你也应该能明白在一个十几人的团队里去做一个大规模的自动化测试项目回报是追不回投入的这样的项目即使能立项最后也是死掉。反过来如果你的领导愿意支持你去做一个复杂的系统那他要么有一个推广提升的配套计划要么是一个技术fans像曾经的我一样不尊重自动化测试的本质也会败在ROI的规律手下。
## 思考题
回到这讲开篇的问题思考一下你目前负责的自动化测试项目是overwork还是underwork
欢迎你在留言区和我交流互动,也推荐你把这讲内容推荐给身边的测试同学,一起精进技术。