敏捷自动化测试(1)—— 我们的测试为什么不够敏捷?

测试是为了保证软件的质量,敏捷测试关键是保证可以持续、及时的对软件质量情况进行全面的反馈。由于在敏捷开发过程中每个迭代都会增加功能、修复缺陷或重构代码,所以在完成当前迭代新增特性测试工作的同时,还要通过回归测试来保证历史功能不受影响。为此我们期望:

  • 测试用例要覆盖所有功能;
  • 要在各种可能的环境下作兼容性测试;
  • 系统的稳定性、性能都要测试;

测试频率足够高:

  • 每日构建产生的版本要保证可用;
  • 每个迭代都需要对历史功能做回归测试;
  • 释放前或上线后如果打了补丁,就需要回归;

但实际情况往往不遂人愿:

实际测试周期变短:

  • 上线时间提前确定,研发进度延期,测试计划被迫延后;
  • 最后阶段经常会临时增加测试任务;
  • 所有人都知道还需要再经过一轮测试,但时间没有了;

有效测试资源稀缺:

  • 临时从其他项目抽调的测试人员不能立刻有效的开展测试工作;
  • “搞不清楚”本项目的研发人员到底是不会做测试还是不愿做测试;

因此由于客观上的资源和时间限制,完整的、及时回归测试在人工测试情况下,往往是不可能完成的任务。团队内部也会产生各种争执:

  • 提交给测试的版本为什么研发人员不先做通过性测试?
  • 这次代码改动量不大,有必要再花那么多时间回归么?
  • 当初不是承诺这次修改肯定不会影响以前的功能么?
  • 怎么不早说要支持Chrome浏览器,现在哪还有时间测试啊?
  • 怎么能让现场出现这种低级的Bug,打补丁后为什么不仔细回归一遍?

争执越演越烈,最终有团队成员爆发了:“这简直不是人干的活!”。

您怎么看待这句话呢?

其实话糙理不糙,用更理性的语言翻译一下就是“有些工作不应该以人工的方式来完成”,比如:

  • 大量机械的、重复性的回归测试;
  • 结果的正确性不依赖主观判断的测试;
  • 需要模拟大量数据、大量并发量的测试;
  • 需要不间断执行的测试(比如7*24小时持续执行);
  • 需要短时间内完成的大量测试用例执行(比如完整的功能回归测试);

通过自动化测试可以极大的提升回归测试、稳定性测试以及兼容性测试的工作效率,在保障产品质量和持续构建等方面起到举足轻重的作用。特别是在敏捷开发模式下,自动化测试是必不可少的。

目前业界的商业/开源自动化测试工具有很多,比如,功能测试工具有QTP、Selenium等,性能测试有LoadRunner、JMeter等。其工作原理无非都是通过“测试脚本”和“测试数据”来完成“测试过程”,并比较“测试结果”,进而形成“测试报告”。

本文不对这些测试工具的差异或优劣进行对比,只以作者目前采用的Selenium为例进行分析:敏捷开发模式需要自动化测试,但自动化测试本身的敏捷性又如何呢?

Selenium是针对Web应用的开源自动化测试工具,通过编写模拟用户操作的脚本,它会打开浏览器对Web应用进行黑盒测试。可以方便的用于功能测试、兼容性测试、 稳定性测试及并发测试。目前已被主流浏览器厂商广泛支持,同时也是很多其它自动化测试工具(比如,RobotFramework)的底层核心技术。Selenium由IDE、Remote Control(简称RC)、WebDriver、Grid四个工程组成:

  • Selenium IDE

是一个用于录制/回放测试脚本的Firefox附加组件,录制的脚本可以生成基于Selenium RC的测试代码(Java、Ruby、C#等)。适用于快速入门,不太适用于实际较大的测试项目;

  • Selenium RC

RC由Server和Client组成两部分组成,Server负责加载/关闭浏览器以及作为HTTP代理来访问Web应用,Clinet支持多种编程语言和测试框架(TestNG、JUnit、NUnit等)。

  • Selenium WebDriver

WebDriver作为Selenium2的核心特性提供比RC更简洁易用的API,是官方推荐的RC替代方案。可以更好的支持动态网页,不需要再额外启动一个独立的Server。

  • Selenium Grid

是Selenium的一个扩展工具,可以很方便地同时在多台机器上和异构环境中并行运行多个RC或WebDriver用例。

Selenium RC是通过在浏览器加载时“注入”JS函数来操纵后续的浏览器行为,Selenium WebDriver则通过直接调用各个浏览器内置的本地事件来达到这一目的。WebDriver目前已经作为W3C规范草案,提交由Google、Mozilla等浏览器厂商讨论。

WebDriver规范定义一组与平台、语言无关的接口,包括发现和操作页面上的元素以及控制浏览器行为,主要用于支持Web应用的自动化测试。WebDriver的核心是通过findElement方法返回DOM对象(WebElement),通过WebElement可以对DOM对象进行操作(获取属性、触发事件等)。其中findElement方法需要的元素定位器(Locator)支持ID、XPath、CSS、超链接文本等多种方式。

“WebDriver”顾名思义就是“Web浏览器驱动”,它专注于解决如何通过外部命令(通常为测试用例)操作浏览器的问题。至于测试用例按照什么顺序执行、执行过程中如何传递数据、测试结果如何断言、如何报告,则可以通过集成其它优秀的专业测试框架(比如,TestNG)来实现(WebDriver没有必要重复造轮子)。

下面用以“用户管理”为例,来看看用WebDriver实现的“增加”和“删除”测试脚本(只示意部分关键代码)。

1、在用户列表页面点击“新增”按钮,跳转到新增用户页面:

webDriver.findElement(By.xpath("//a[contains(@id,'addUserBtn')]//button")).click();.

脚本解读:

  • By.xpath()表示通过XPath来定位页面元素;
  • click()表示在找到的当前控件上执行点击操作;

2、在新增用户页面,输入“帐号”、“密码”、“姓名”,选择“性别”、“生日”和“国籍”,然后点击“保存”按钮,回到用户列表页面,并判断是否增加成功:

1)  String account="autotest2";
2)  webDriver.findElement(By.xpath("//div[contains(@id,'account_userForm')]//input")).sendKeys(account);
3)  webDriver.findElement(By.xpath("//div[contains(@id,'password_userForm')]//input")).sendKeys("1");
4)  webDriver.findElement(By.xpath("//div[contains(@id,'name_userForm')]//input")).sendKeys(account);
5)  webDriver.findElement(By.xpath("//div[contains(@id,'sex_userForm')]//input")).click();
6)  webDriver.findElement(By.xpath("//span[text()='女']")).click();  
7)  webDriver.findElement(By.xpath("//div[contains(@id,'birthdate_userForm')]//input")).click();
8)  webDriver.findElement(By.xpath("//div[contains(@id,'nationality_userForm')]//input")).click();
9)  webDriver.findElement(By.xpath("//span[text()='中国']")).click();     
10) webDriver.findElement(By.xpath("//a[contains(@id,'userSaveBtn')]//button")).click();        
11) WebElement ele = webDriver.findElement(By.xpath("//div[text()='"+account+"']"));    
12) Assert.assertNotNull(ele);

脚本解读:

  • sendKeys ()表示在找到的当前控件上输入字符;
  • 2~9行表示通过输入或点击选择的方式为用户相关属性赋值;
  • 第10行表示点击“保存”按钮(点击后会自动转向用户列表页面);
  • 11~12行表示查找页面上文本内容为新增帐号的div,并断言该div是存在的(不为空);

3、删除刚刚增加的人员,然后判断是否删除成功:

1)  webDriver.findElement(By.xpath("//a[contains(@id,'deleteUserBtn')]//button")).click();
2)  WebElement ele = webDriver.findElement(By.xpath("//div[text()='"+account+"']"));
3)  Assert.assertNull(ele);

脚本解读:

  • 第1行表示点击“删除”按钮;
  • 2~3行表示查找页面上文本内容为新增帐号的div,并断言该div已经不存在了(为空);

通过上面的脚本就可以实现“用户增加、删除”的自动化测试,并且可以跨浏览器。看到这里您会不会觉得整体还不错,如果测试脚本再能通过录制的方式自动生成就更好了!

“看”起来确实还不错,但在实际项目中用起来就没那么爽了。这其实是在技术/工具选型时普遍存在的现象:在验证/试用阶段的评价很高,但在投入生产使用时会遇到各种各样的问题,因此大家在选型阶段除了考虑功能,还要考虑技术/工具本身的开放性和可扩展性。

上面的方案单纯从技术的角度来讲是很不错的:开源、社区活跃、标准化程度高、支持跨浏览器、脚本回放稳定、可集成性高,等等。

但是如果就这样应用在实际项目中,会从过程的角度暴露一些棘手的问题:

  • 测试脚本是“代码级”的,那么应该由谁来编写呢?

测试人员和开发人员好像都有编写这些测试脚本义务,但似乎又都有不写的理由。

  • 测试脚本必须不断的修改才能在最新版本上跑通,谁该为此买单?

在敏捷开发过程中需要快速响应需求的变化(新增、变更),这一点整个团队都好理解。因此如果需求发生变化,开发人员修改代码、测试人员修改测试脚本,一切顺理成章,大家相安无事。但是在用户需求没有变化时,开发人员频繁修改代码的情况也很常见:比如,修改Bug、针对“坏味道”做重构、调整页面布局或样式。于是在“毫无征兆”的情况下,测试脚本又无法执行了!

这时候测试人员可能会质问开发人员:“做之前怎么不想清楚?都已经测试完成的功能,为什么还总是反复修改?什么时候代码才能稳定?”。

而开发人员此时也会非常理直气壮:“有Bug能不改么?页面布局不合理导致用户体验差,能不改么?而且敏捷中的一个重要的实践就是重构啊”。

大家又是似乎都有道理、也都有苦衷。我虽然作为测试人员,但是在这个问题上还是“偏向于”开发人员的: 在软件生命周期的各个阶段(需求、设计、开发、测试)中,后面的阶段对前置阶段是有一定依赖的,所以越往后期响应变化的难度越大。比如,在“开发”环节不仅需要响应“需求”的变化(新增、变更),而且需要响应“设计”的变化。从这个角度来看,“测试”本就应该响应“开发”的变化。

对于在实际项目自动化测试过程中遇到的上述问题,归根到底是因为“自动化测试方案本身的敏捷程度不够”,主要体现在如下三个方面:

1、 学习成本高

测试人员除了要掌握WebDriver接口之外,还要掌握XPath、TestNG的用法,甚至还需要对功能的前台实现有一定了解。

2、 脚本维护困难

敏锐的开发/测试人员从上面的示例脚本中,可以马上嗅出一些“坏味道(Bad Smell)”: 代码相似度非常高、可能变化的数据被硬编码在测试代码里、代码可读性差、测试代码与页面源码耦合度大,等等。这些坏味道的出现,通常意味着需要进行重构,否则会愈演愈烈,最终变得尾大不掉。

【注】业界常见的测试工具本质上还是针对页面源码来编写(或生成)测试脚本的,即使提供了录制工具,此类脚本的可读性和后期可维护性还是非常差的。

3、 断言条件繁琐

业界常见的测试工具即使提供录制脚本的功能,但是对于“断言”还是需要人工插入的(工具做不到智能的判断我们想要在哪里设置断言),于是断言就成了自动化测试人员的“噩梦”。

断言对象可能很“多”,页面的信息量往往很大,需要在测试脚本中为每个断言对象(比如,页面某个文本框的值)补充断言语句。

预期结果是可能“变”化的,甚至是动态的,因此预期结果的值如果与脚本逻辑耦合在一起,将来极难维护。 断言机制比较“呆”板,对于 未设为断言对象的字段,如果发生错误也是无法感知的,并且难以对于UI样式或UI逻辑(比如,翻页图标应该灰显)进行断言。

换个角度可以理解为,如果这样的断言条件“多”的话,整个测试用例集会“变”的非常“呆”板!

想要有效的改善这些问题,就必须让自动化测试变得“敏捷”起来!

转载于:https://www.cnblogs.com/xinleishare/p/4256670.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/376300.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习c++

目录 一 、 boost库: 1. 多线程 c boost多线程学习(一) 二 、数据库: 三、socket编程: c socket学习(1.1) c socket学习(1.2) c socket学习(1.3&#x…

mysql5.6与mysql5.5不同

1.编译阶段 要明白with与without的区别,选项值分1和0,或者对应为on或off,代表支持与不支持;with的1(on)与without的0(off)是同样的,with的0(off)与…

c++ 基本排序算法学习

C实现排序算法 代码地址 vector<unsigned int> cVec; int nSize cVec.size();1 冒泡排序 算法思路&#xff1a; 每两两相邻的数值都会比较大小&#xff0c;前面比后面大的时候就交换位置&#xff0c;否则就不动。 代码&#xff1a; void BubbleSort() {//优化&#x…

ios 程序学习

马上着手开发iOS应用程序&#xff1a;五、提交应用与寻找信息 2013-01-11 15:36 佚名 apple.com 我要评论(0) 字号&#xff1a;T | T本文介绍了您已经学习完如何开发一个优秀的iOS应用之后&#xff0c;应该掌握的内容&#xff0c;包括将您的应用提交到App Store让其他人下载&am…

解决SimpleButton被移除后保持OVER状态

假设场景中有一SimpleButton叫testBtn,执行下面操作&#xff1a;1.鼠标移上testBtn&#xff0c; testBtn状态变为OVER2.移除testBtn&#xff0c;removeChild(testBtn)3.5秒后重新添加testBtn到场景此时&#xff0c;看见testBtn还是OVER状态。解决方法&#xff1a;1.记录testBtn…

c++ socket学习(1.1)

本文学习相关资料&#xff1a; C/C socket编程教程 环境&#xff1a;vs2015 源码&#xff1a;本文代码 windows 如何创建客户端与服务端通信&#xff1f; TCP&#xff1a; 服务端 在windows先告诉程序我们要使用哪个版本的winsock&#xff0c;成功调用了它才能继续下去 #…

c++ socket学习(1.2)

本文学习相关资料&#xff1a; C/C socket编程教程 环境&#xff1a;vs2015 源码&#xff1a;本文代码 windows 如何创建客户端与服务端通信&#xff1f; UDP&#xff1a; 这次就没什么客户端服务端好说了&#xff0c;UDP是没有无连接的 所以改叫接收端和发送端吧 接收端 …

js高级功能与高级需求、高级期待

http://www.cnblogs.com/leadzen/archive/2008/02/25/1073404.html 简单练习题&#xff1a;http://tieba.baidu.com/p/2189347922 ---------------------- scope链 闭包 Javascript属性prototype node.js metaprogramming AMD、CMD机制 http://www.makumo.com/js-modules-amd-c…

synchronized同步锁

在多线程的情况下&#xff0c;由于同一进程的多个线程共享同一片存储空间&#xff0c;在带来方便的同时&#xff0c;也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突&#xff0c;有效避免了同一个数据对象被多个线程同时访问。由于我们可以通过 private…

c++ socket学习(1.3)

本文学习相关资料&#xff1a; C/C socket编程教程 环境&#xff1a;vs2015 源码&#xff1a;本文代码 在这里c socket学习&#xff08;1.1&#xff09;学到了怎么样建立TCP&#xff0c;然后通过TCP连接发送、接收信息。 但是都是一次性的&#xff0c;当时是接收信息后就结束…

一个一线城市的IT白领的生活成本:3万/年

自从大学毕业&#xff0c;经济独立&#xff0c;就开始全面统计各种生活开支。仔细的去统计下&#xff0c;发现开销还是挺大的。 定理&#xff1a;开销越大&#xff0c;就意味着你每个月的收入必须越高。 三族鼎立节余族: 收入-开支 > 0月光族&#xff1a;收入-开支 0透支族…

android 编译共享ccache的缓存

1. android自带的ccache版本号(2.4版本号)过低&#xff0c;是无法支持以上的功能的&#xff0c;须要使用新版ccache。2. 最新的ccache请到http://ccache.samba.org/download.html下载3. 下载解压之后&#xff0c;在linux底下进入ccache文件夹&#xff0c;执行:./configure./mak…

一位软件工程师的6年总结

作者&#xff1a;成晓旭 “又是一年毕业时”&#xff0c;看到一批批学子离开人生的象牙塔&#xff0c;走上各自的工作岗位&#xff1b;想想自己也曾经意气风发、踌躇满志&#xff0c;不觉感叹万千……本文是自己工作6年的经 历沉淀或者经验提炼&#xff0c;希望对所有的软件工…

c++ socket学习(1.4)

本文学习相关资料&#xff1a; C/C socket编程教程 环境&#xff1a;vs2015 源码&#xff1a;本文代码 前面学到了TCP怎么循环发包&#xff0c;但是TCP连接的话会出现一个问题粘包。 TCP连接接收到的数据并不是马上读取到内存里面的&#xff0c;而是放在缓冲区&#xff0c;让…

mongodb中分页显示数据集的学习

mongodb中分页显示数据集的学习 这次继续看mongodb中的分页。首先依然是插入数据&#xff1a; 1&#xff09; db.Blog.insert( { name : "Denis", age : 20, city : "Princeton" } ) db.Blog.insert( { name : "Abe", age : 30, city : &quo…

学习编程,英语很重要!!

学会编程&#xff0c;可能不需要英语多好&#xff0c;但是学号编程&#xff0c;英语真的很重要&#xff01;&#xff01;&#xff01; 好多文档&#xff0c;demo全是英文的&#xff0c;蛋疼&#xff0c;应许需要学习&#xff01;&#xff01;&#xff01;转载于:https://www.cn…

c++ socket学习(1.5)

本文学习相关资料&#xff1a; C/C socket编程教程 环境&#xff1a;vs2015 源码&#xff1a;本文代码 这次来试一下使用TCP来传输文件&#xff0c;其实传输数据和差不多&#xff0c;就是多一个读取文件&#xff0c;和一个写文件而已。 服务端 int readlan 100; std::ifst…

matlab生成HEX文件-任意信号 大于64K长度

HEX文件格式不赘述&#xff0c;写里直接放上代码。请批评改正。 1 %%convert a signal data into hex file format2 % data format:16bit 3 % signal length: less than 2^24-14 % author: Yang Li yangli0534gmail.com5 % data:2015.01.276 7 clear all;8 close all;9 clc; 10…

移动端网页中ViewPort的使用

<meta name"viewport" content"widthdevice-width,target-densitydpihigh-dpi, initial-scale1.0, minimum-scale1.0, maximum-scale1.0, user-scalableno"> <meta name”viewport” content”widthdevice-width, initial-scale1.0, user-scalabl…

c++ socket学习(1.6)

本文学习相关资料&#xff1a; C/C socket编程教程 环境&#xff1a;vs2015 源码&#xff1a;本文代码 这次来看看UDP 之前在c socket学习&#xff08;1.2&#xff09;讲过UDP怎么发送了&#xff0c;那现在来做一个可以一直发送的。 这次没有什么接收端和发送端了&#xff0…