qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

自动化测试技巧之结果验证

 自动到了京城,已经两月没有好好整理下自己的思路了,也没有好好的写一些东西了。现在,真应该回归了。因为有的东西,不吐不快。
  ---------------------------------------------------------------
  在这里整理下关于自动化测试技巧中的结果验证的一些东西吧,这块也是我当年一个一个坑走出来的,虽然现在还有很多坑在等着我。
  在我们编写测试脚本的过程中,很多时候总会不知道该怎么去判断我们的结果是否正确。因为有一些操作涉及图形、一些操作涉及页面窗口变化、一些则涉及数据库、系统事件等,从而导致我们无处下手,很多失败的自动化测试也是因为测试结果的不准确判断造成的,所以,在此要慎重选择。
  首先来说说文本的验证吧。这也是目前最简单也是最有效的判断条件之一了。
  一般文本验证的做法是:1. 获取当前页面上要验证的文本字符串并赋值给一个变量;2. 读取预期结果的文本字符串;3. 进行比较
  在这里需要注意的一些问题有:
  1) 字符串前后的空格。因为有些时候我们获取到的一些字符串总会在前后出现一些非正常的空格,尤其是url;所以在对这些进行判断的时候,我们需要进行预处理,即去掉前后的空格。vbs中可以使用trim函数;
  2) 文本的特殊字符;
  3) web上有一些提示内容,如账号密码输入错误和非空的提示信息,都是很多条显示在同一个位置的,并且显示的方式为webelement.show()、webelement.hind(),对于这种文本的判断,最好结合web本身的一些方法来写方法实现,尽量避免过多依赖于测试工具,如qtp、winrunner;
  再来说下页面和窗口变化的验证,这类也是很常见的验证方式了。
  不过,即使是千里马也有马失前蹄的时候,所以,我还是尽可能的整理总结下我所遇到的和想到的东西吧。
  页面跳转一般分为页面内跳转和页面外链接,其中页面内跳转属于同步操作,需要等待执行完成才能进行下一步操作;页面外链接则属于异步操作,只要发送出打开浏览器,访问地址的操作就可以尽心下一步操作,不用等待链接的地址是否正确响应。因为实现机制的不一样,所以测试时候的判断方式也有一些区别。
  页面内跳转的在click元素后需要执行一个等待同步操作,常见的web等待同步有Browser.sync、while oIE.busy:wend、while oIE.state <> 4 :wend,如果不加等待时间,则测试脚本会在忽略加载时间直接往下执行。这时候,如果你的网速够坚挺,那么一切好说,如果不坚挺,那就会报错了。页面外链接除了需要等待页面加载之外,还必须对浏览器窗口进行定位,一般定位的方法主要有hwnd、index、name、title等,定位方案不选好,遇到稍微奇怪点的情况就会悲剧。
  窗口变化一般表现在全屏/非全屏,大小、窗体元素改变等地方。这些都可以通过获取窗体的属性来进行判断。不过全屏的判断方式比较特殊,因为很多窗体没有提供全屏的属性值,在这里我们可以通过获取当前窗体的宽高,然后和当前屏幕的availableScreen进行比较,切记,窗体的全屏指的是可用屏幕,就是除掉工具导航栏外的部分,不要真以为全屏就是满屏幕了。而浏览器的全屏则指的是浏览器显示区域大小为全部屏幕,就是除掉浏览器工具菜单栏的部分占了屏幕的全部显示区域。
  再来说说数据变动部分的测试验证。
  数据变动一般分为在本地数据库和服务器数据库改变、本地数据文件改变和服务器数据改变。对于这种数据的改变,通常的做法都是先获取数据内容,然后再和预期内容进行比较。在测试数据相关的功能时,很多人都会有种感觉,不穷尽数据组合很难完全保证是否正确。然而事实上,涉及数据变动的功能在测试时一般都是分为两部分,即数据测试和功能测试
  举例子来说:一个出生地选择的功能,有省市县三个下拉列表,为三级联动。那么在测试的时候,就需要先对三级联动这个功能进行测试;确保功能没问题之后,再对数据进行测试;最后在进行一个整合的随机测试。这样就能够保证在短时间内保证这个功能和所有涉及到的数据完全正确了。在设计自动化测试脚本的时候,也可以分别针对功能和数据进行设计编写,以便于我们在遇到错误时进行定位。
  最后,来说说系统事件的验证技巧。
  系统事件的测试验证,首先我们得知道系统事件到底做了哪些操作和修改,并针对这些系统级的操作和修改进行验证,以避免只依赖于被测程序的提示,从而提高测试脚本的准确性。
  获取系统事件,可以利用安装测试工具(如InstallWatch)来进行跟踪。跟踪之后的验证就比较简单了,只需要读取修改的内容和预期结果进行比较就可以了。但是有一些系统文件,我们无法直接读取到里面的内容,并且这些文件也不需要或者不给我们开放一些权限去读取文件内容,那该怎么办呢?通常的解决方法有:1. 先对该系统事件进行单独的测试,以确保该功能正常;2. 对被测程序的操作进行测试,以确保被测程序能够正常操作系统事件;3. 设计测试脚本在被测程序和系统级分别进行验证,其中被测程序验证系统事件的提示信息,系统级则获取系统文件的最近修改事件和修改者,并与当前操作事件进行对比,如果一致,则可以认为这个操作是正常执行完成了的。
  好了,结果验证就到这里。
相关文章
自动化测试技巧之图片验证

posted @ 2013-12-13 09:53 顺其自然EVO 阅读(362) | 评论 (0)编辑 收藏

2013年度持续集成实践小结[2] —单元测试

  前文提到,在UI自动化之外,我们着力探索了如何实施单元测试(unit test
  相对于UI自动化,单元测试方面的实践还是不够充分的,因此,这里也只是小结一下我们的经验
  概述
  首先明确一下,此处单元测试概念与经典意义有所不同,泛指所有:
  由开发工程师编写的,可以在开发本地一键运行的,运行时间在分钟级别的测试用例,用例执行会依赖不多的,但往往也是稳定可靠的外部环境
  测试框架一般使用TestNg而不是JUnit,主要原因在于TestNg的 DataProvider 功能很给力,非常适合用例须要覆盖多分支的场景
  用例组织原则:
  一个测试类对应一个功能类: funcOneTest.java 对应于 funcOne.java
  若干个测试方法对应一个功能方法:test_funcOne_smoke() & test_funcOne_normal() & test_funcOne_error() 对应于 funcOne()
  用例分类约定
  还是以 超市购物 为背景,写几个Demo用例
  Δ冒烟型用例 – 甲
@Test(description = "getNewestItems_冒烟_获取最新商品并检查若干关键属性")
public void test_getNewestItems_smoke() {
List<ItemVo> itemList = itemBean.getNewestItems(1);
Assert.assertTrue(itemList.size() == 16,        "size应该是16");
for (ItemVo vo : itemList) {
Assert.assertTrue(vo.getName() != null,     "name不能为空");
Assert.assertTrue(vo.getPrice() != null,    "price不能为空");
}
}
  说明:
  顾名思义,就是在用例中简单调用一下被测方法(method),主要是为了跑通流程,拒绝Block级别问题
  实践中,推荐 伴随着功能开发,随时编写冒烟用例,功能代码与冒烟用例一起提交代码库
  对于一些复杂的功能,功能开发的过程也是一个持续重构的过程,编码前期完成的冒烟用例,可以有效起到安全网作用
  Δ冒烟型用例 – 乙
@Test(description = "enter_and_leave_market_冒烟_进入与离开超市")
public void test_enter_and_leave_market_smoke() {
Custom tom = new Custom("Tom");
tom.enterMarket();
Assert.assertTrue(Custom.isAtMarket(tom),   "tom应该在超市内");
tom.leaveMarket();
Assert.assertFalse(Custom.isAtMarket(tom),  "tom应该不在超市");
}
  说明:
  这个用例把进入超市及离开超市这两个(强相关的)接口串起来了,目的在于走通流程
  实践中,建议 将新模块(函数)与其强相关的模块(函数)尽早进行简单的集成测试以提前发现一些问题

 Δ正常流程用例
@Test(description = "addToCart_正常流程_往购物车内添加各种类型数目的商品", dataProvider = "test_addToCart_normal_data")
@Rollback
public void test_addToCart_normal(String caseNote, long itemId, int count) {
Custom tom = new Custom("Tom");
this.setCustom(tom);
cartBean.addToCart(itemId, count);
Item item = tom.getCart.getItems.get(0);    // 获取购物车中的第一项商品
Assert.assertEquals(item.getId,      itemId,  "itemId is wrong");
Assert.assertEquals(item.getCount,   count,   "count is wrong");
}
@DataProvider
public Object[][] test_addToCart_normal_data() {
return new Object[][] {
// caseNote,                    itemId,     count
{"Milk  - just a dozen",        39001L,     12,  },
{"Bread - huge number",         116001L,    999},
{"Bean  - less then 10",        1018100L,   2},
};
}
  说明:
  这个示例代码演示了如何测试addToCart()的功能,假定这个方法内部有十分复杂的业务逻辑,我们须要覆盖各种场景
  可以看到,正常流程用例与冒烟用例其实差不多,不同的是,正常流程用例会覆盖更多分支,冒烟用例则一般是走通流程就行
  这个用例使用了@Rollback标签,用例执行后会回滚数据,而不会真正往数据库内插入数据;这个功能十分有用,可以大大减少数据准备与清理的工作;至于@Rollback背后的实现原理,此处暂时按下不表
  测试参数使用TestNg的@DataProvider组织起来,每一行都是一组测试数据,覆盖一种测试分支
  测试参数的第一列建议设置为caseNote,简单阐述用例的意图,可以有效提升用例可读性
  Δ异常流程用例
@Test(description = "addToCart_异常流程_往购物车内添加参数非法的商品", dataProvider = "test_addToCart_error_data")
@Rollback
public void test_addToCart_error(String caseNote, long itemId, int count, int expectedErrorCode) {
Custom tom = new Custom("Tom");
this.setCustom(tom);
try {
cartBean.addToCart(itemId, count);
Assert.fail();
} catch (Exception e) {
Assert.assertEquals(e.getErrorCode, expectedErrorCode);
}
}
@DataProvider
public Object[][] test_addToCart_error_data() {
return new Object[][] {
// caseNote,                        itemId,     count,  expectedErrorCode
{"iPad       - 0 count",            39001L,     0,      Cart.ZERO_COUNT},
{"MacBookPro - more then stock",    116001L,    1024,   Cart.MORE_THAN_STOCK},
{"no such item",                    0L,         1L,     Cart.NO_SUCH_ITEM}
};
}
  说明:
  如果不同的异常输入会有相应的 errorCode 的话, 可以把errorCode当成测试数据的一项参数传进去

 Δ特殊场景 & 复杂流程
  个别特殊场景,不方便使用@DataProvider合并的复杂流程,可以单独创建一个用例进行测试,函数命名的时候注明一下,例如:
  test_addToCart_error_withoutEnoughMoney() – 钞票不够
  test_addToCart_normal_mergeMultiCarts() – 合并多个购物车
  Δ前事不忘,后事之师
  出过Bug的地方(及其周边)补充单元测试覆盖,由单元测试帮你记住前事 – test_funcOne_issue12345_bugfix()
  用例编写流程
  用例编写顺序:
  开发新功能时,同步编写冒烟测试,用于自测及调试,功能代码与冒烟用例一起提交 – test_funcOne_smoke()
  稍晚,补充更多分支覆盖的正常流程用例,相当于进行又一轮自测 – test_funcOne_normal()
  最后,补充异常流程和特殊(复杂)场景用例 – test_funcOne_error() & test_funcOne_specialScenario()
  之所以这样安排,一个很重要的原因是希望 保持主干及正常流程的畅通,确保开发及测试不会Block
  此外:
  尽量把单个开发任务切分成多个小功能点,频繁提交,稳扎稳打,配合Jenkins & Sonar,多跑单元测试和静态代码检查,问题早发现早处理
  前事不忘,后事之师,出过Bug的地方补充单元测试
  补遗
  单元测试与静态代码检查(static analysis, SA)是一对好基友,两者可以统一显示在Sonar上面,在实践中往往一起考察
  关于Sonar,实乃居家必备,代码度量之利器,以后会另外讲述,这里先贴个图:
相关文章:

posted @ 2013-12-13 09:49 顺其自然EVO 阅读(213) | 评论 (0)编辑 收藏

百万连接之路

  前段时间接到某项目中关于虚拟机所在宿主机上最大支撑连接数的测试需求。应用场景类似于在物理机上运行着多个虚拟机,这些虚拟机对外提供服务,来自于任何地方的客户端都可能向这些应用服务发起连接和请求。也许单个虚拟机并发的连接数十分有限,但对提供虚拟机服务的物理机或宿主机来说连接数就可能达到十万、几十万甚至百万。在这样的情况下,宿主机是否能够稳定运行呢?同时项目方也提出了明确的测试目标,支撑300万连接,这就需要我们着手进行测试验证了。
  首先,我们先做一个假设,即这300万连接里同时只有少数活动连接,测试场景可以简化为保持300万长连接的测试。明确了这一点,我们继续分析测试可能出现的瓶颈点,是CPU、内存还是网络?借鉴以往长连接或者消息推送类服务的测试经验,由于保持长连接并不需要消耗过多CPU和网络资源,只是占有系统的内存,我们决定把关注点主要集中在内存使用上,测试过程中也确实出现了一些内存相关的问题,这是后话了。
  我们首先需要准备一个测试环境,一个可以支撑足够连接的应用服务,大量的客户端。很幸运我们可以借用杭研前台技术中心基于node.js开发的开源游戏引擎pomelo作为服务端程序。使用java网络API开发了一个简单的客户端程序。客户端向服务器发起连接请求,成功后把连接对象保存在内存中。至此万事具备,只欠执行测试了。
  不急,我们先建立100万连接试试。没过多久第一个拦路虎就出现了,在客户端日志里出现了java.net.NoRouteToHostException异常,将它作为关键字google一把,原来是/proc/sys/net/ipv4/ip_local_port_range配置的区间太小,端口耗尽导致,配置修改如下
pomelo@debian:~/socktest$ cat /proc/sys/net/ipv4/ip_local_port_range
2048 65500
  可见单个客户端ip只能建立6万多连接,所以我们需要大约50个独立ip发起300万的连接,为简单起见我们使用50台虚拟机运行客户端,而没有采用单机多ip的方案。
  继续测试,再次被异常打断,java.net.SocketException: Too many open files。这个有经验的同学都应该了解,该修改ulimit了。改完继续,可奇怪的是运行了一段时间后,又被同样的异常中断了,不是修改得很大了吗?怎么又出了呢?确认修改有没有生效,/etc/security/limits.conf文件是否保存,不会是这么狗血的问题吧。确认已经保存,检查了客户端进程的limit,/proc/pid/limits,发现open files竟然只有4096。百思不得其解。最后还是SA发现了问题的原因,原来是open files默认最大值1024*1024,我们修改时设置过大导致其溢出了,最后在/etc/security/limits.conf中添加如下两行搞定
* hard nofile 1048576
* soft nofile 1048576
  至此我们终于完成了100万连接的阶段性胜利,不能松懈, 一鼓作气拿下300万大关。等等,有人可能会问,服务端的open files也是配置1048576,就100万多点,肯定不能支撑300万连接吧。是的,open files是针对单一进程的限制,但我们跑的服务是多进程程序,所以不用担心。另外,open files的最大值也能通过配置/proc/sys/fs/nr_open参数修改,这样就能摆脱1048576的上限了;而系统中所有进程打开的文件数确实是需要配置的,通过fs.file-max修改。
  接着我们遇到了这次测试中最可疑的问题之一。当建立完200万连接以后,我们kill掉了一个服务进程,没过多久,就发现部分运行客户端的虚拟机不能ssh登陆了。通过vnc连接上后发现虚拟机CPU几乎跑满了,dmesg中存在Out of socket memory这样的错误信息。此处省略一千字某SA大神的问题定位过程,本质上是由于服务端和客户端之间存在4万连接,服务进程挂掉后客户端机器收到大量FIN包导致网络相关内存溢出,而客户端机器上存在一个使用curl的定时任务,网络内存溢出引发其某个bug,进而引发CPU跑满。因此调大net.ipv4.tcp_mem配置,注意该参数的单位是页,而不是字节。
  眼看着300万连接的目标已经近在咫尺,可问题再次不请自来了。在漫长的等待后,我们用ss –s确认最终建立的连接数,但这个数值却始终停留在280万附近,照例的打开dmesg查看,发现了一大堆错误信息,如下为开始的一段
[531503.634391] TCP: too many of orphaned sockets
[531503.634412] TCP: too many of orphaned sockets
[531503.634432] TCP: too many of orphaned sockets
[531503.634451] TCP: too many of orphaned sockets
[531508.704084] net_ratelimit: 255864 callbacks suppressed
[531508.704088] Out of socket memory
[531508.704233] Out of socket memory
[531508.704245] Out of socket memory
  简单的调大net.ipv4.tcp_max_orphans参数,问题依旧。查看服务端日志,发现报错
  FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
  查看服务进程数,确认有部分进程已不存在了,结合以上两种日志不难猜到问题的根源,是由于服务进程挂掉,瞬间出现大量孤儿连接,进而导致网络内存溢出引起。但服务进程为什么会莫名其妙地挂掉呢?咨询了相关开发人员,由于pomelo是基于node项目的,需要通过编译选项调整内存上限,修改重新编译后测试,进程还是出现了内存溢出的情况,但明白了问题的本质,我们通过增加node进程数量,以减小单个进程的内存占用,可以绕过该问题。而node进程的内存限制问题还需要后续的确认。
  到此为止,终于完成了300万连接的目标,可喜可贺。简单总结一下,连接数测试不同于常规的性能测试,不太关注TPS和响应时间等指标,主要是通过dmesg和日志中出现的异常信息定位问题,进而调整系统相应参数,这些参数大多与网络、句柄数及内存有关。不仅仅是测试阶段,日常运维这类系统也应该时刻关注这些。
相关文章
百万连接测试之方法篇

posted @ 2013-12-13 09:32 顺其自然EVO 阅读(601) | 评论 (0)编辑 收藏

Java String 的十大常见问题

以下是Java字符串经常被问到的排名前十的问题。
  1、如何比较字符串?使用 “==”  还是 “equals()”?
  简单来讲,“==”比较的是引用(对象的内存地址),“equals()” 比较值是否相等。除非你想检测两个字符串是否是同一对象,否则都用equals()。
  当然了解字符串池的概念更好。
  2、为什么对于安全敏感的信息使用char[] 优于String ?
  Java 中字符串是不可变的,这就意味着一旦他被创建,在垃圾回收器收集之前都是一样的。使用数组你可以明确的修改它的元素,以这种方式,安全敏感的信息(如:密码)将不存在于系统的任何地方。
  3、switch 语句中是否可以用String ?
  在jdk7中是可以的。在jdk6以前的版本都不可以。
// java 7 only!
switch (str.toLowerCase()) {
case "a":
value = 1;
break;
case "b":
value = 2;
break;
}
  4、如何将String 转换为 int ?
  int n = Integer.parseInt("10");
  很简单、很常用以至于有些时候都忽略了她。
  5、如何使用空白字符拆分字符串?
  只需要简单的使用正则表达式“\s” 。 “\s” 代表着空白字符有“ ” , “\n” , " \r" , "\t" 。
  String[] strArray = aString.split("\\s+");
  6、substring()真正做了些什么?
  在Jdk6 中substring()方法提供了一个表示目前字符数组(该字符数组表示当前字符串)的窗口,但是并没有创建一个新的字符串。为了创建一个新的字符串,可以在substring()后面添加一个空字符串,如下所示:
  str.substring(m, n) + ""
  以上代码会创建一个字符数组表示新的字符串。以上方法在有些时候可以提高代码的效率,因为垃圾回收器可以回收大的String ,而只保存很少的子字符串。
  在oracle的JDK7中,创建了一个新的char数组,而不是使用原来的char数组。关于该部分内容可以参见JDK6和JDK7中substring的区别

  7、String VS StringBuilder VS StringBuffer
  String VS StringBuiler : StringBuiler是可变的,这意味着创建以后还可以改变他的内容。
  StringBuiler VS StringBuffer : StringBuffer 是同步的(线程安全),但是比StringBuiler慢。
  8、如何重复字符串?
  在python中我们是需要对字符串乘以一个数就可以表示重复字符串。在Java中可以使用Apache Commons Lang package 中StringUtils 的repeat方法。
String str = "abcd";
String repeated = StringUtils.repeat(str,3);
//abcdabcdabcd
  9、如何将字符串转换为Date对象
String str = "Sep 17, 2013";
Date date = new SimpleDateFormat("MMMM d, yy", Locale.ENGLISH).parse(str);
System.out.println(date);
//Tue Sep 17 00:00:00 EDT 2013
  10、如何计数一个字符在字符串中出现的次数
  使用Apache Commons Lang package 中StringUtils类。
int n = StringUtils.countMatches("11112222", "1");
System.out.println(n);

posted @ 2013-12-12 11:21 顺其自然EVO 阅读(469) | 评论 (0)编辑 收藏

LoadRunner测试ajaxweb程序攻略

  用loadrunner测试WEB程序的时候总是会碰到AJAX或者ActiveX实现的功能,而通常这些功能会包含很多客户端函数(一般为JavaScript)。我们该如何处理?如果从功能实现的角度去考虑这个问题,就会走入一个死胡同,因为loadrunner对于JavaScript是无能为力的。如果从loadrunner的本质出发,那么该问题就迎刃而解。因为loadrunner是基于协议来测试的,不管AJAX或者ActiveX在客户端做了多少事情,只要它最后是通过HTTP协议来发送请求的,我们模拟这个请求就可以完成测试。接下来就分析一下如何处理这类问题,以电子病历BS版医嘱录入为例(医嘱录入部分使用了ActiveX技术和AJAX技术)。
  1、录制脚本,分析脚本。
  2、调试脚本。发现脚本能回放成功,但是数据没有提交。此时我们需要查看录制时HTTP请求是什么样的。
  3、修改脚本。我们需要在上图脚本中,在web_custom_request()函数前增加web_add_header("x-ajaxpro-method","SaveOrder");,我们再次回放时,数据就可以写入了。
  其实我们用loadrunner来测试时,更多的要从协议的角度来考虑问题,而非编程角度。

posted @ 2013-12-12 11:14 顺其自然EVO 阅读(178) | 评论 (0)编辑 收藏

2013年度持续集成实践小结[1] —UI自动化

 背景介绍
  按照组织上的安排,咱游击到了S产品(一个快速成长中的Web产品)开搞持续集成
  考虑到S产品核心业务单一明确,前端功能简单,业务逻辑主要在后端的特点,制定了持续集成的实施策略:
  UI自动化为辅,用例少一点,精一点,降低维护成本,用例设计以冒烟和页面跳转,走通业务流程为主,目的是保障一个高可测性的测试环境;
  单元测试重点跟进,自顶向下逐步覆盖各层接口,多覆盖各种分支路径,与UI自动化形成互补。
  这里有个小插曲,我和S产品的测试负责人关于UI自动化用例的粒度和覆盖度有一些歧义,测试负责人坚持UI自动化要尽量覆盖大大小小各种流程与细节,接近于手工测试。最后,咱还是坚持了己见
  虽然,实施策略中设计了以单元测试(这里必须澄清一下,此处的单元测试实为集成测试/接口测试,没有完善的mock,仍有一些外部依赖;但是考虑到使用了JUnit/TestNg,一般也就笼统地称之为单元测试了)为主,后续工作中确实也在这方面寄予厚望,投入较多,但由于种种原因,颇有鸡肋之感,此处暂时按下不表。
  框架选择
  网易杭州研究院开源的,简单易用的,结实耐操的Dagger框架
  用例建模
  考虑到UI自动化在S产品中的预定角色,我们将用例分成三大类:
  UserStory,产品的核心业务流程,一个用例包括一系列步骤,模拟用户在S产品网站的真实运动;这些用例如果失败将明显干扰QA正常测试,一旦上线,将是严重故障;
  Topic,包括一些冗长的特定业务(例如,下图所示的Topic 1:团购超市卡),以及对UI自动化所依赖的外部环境的检测(例如,Topic 2:与供应商保持联系);
  BugCover,出现过线上Bug的功能点补充用例。
Step 1Step 2Step 3Step 4Step 5
UserStory 1从车库进超市取手推车买水果买牛奶现金结账
UserStory 2从正门进超市取购物篮啥都不买就逛逛离开
UserStory 3选购牛奶选购面包购物卡充值购物卡结账
Topic 1团购超市卡
Topic 2与X供货方保持实时联系
BugCover 1央视报道Y牛奶 没有及时下架 导致用户投诉
  这里必须要特别指出的是,不同UserStory的步骤之间 尽量正交,减少重复 ,这样一来:
  在实际编写用例代码时,基本不必考虑是否须要把哪些操作抽象成API,哪些XPath封装起来,完全脚本化写用例;
  相应的,由于用例与用例之间重复代码少,也鲜有共同依赖的API,就可以很放心地将用例责任到人,实现真正的谁写谁负责;
  而对于持续集成来讲,责任到人,及时响应是其能否成功的关键之一。
  另外,我认为这种用例模型仅适合小规模的UI自动化用例,用例数量上去以后,很难实现用例之间的正交。所以,其背后必须有充分的其它类型测试,例如:单元测试/接口测试,作为依托。


 典型的用例结构如下所示(基于TestNg与Dagger)
public class Demo {
BrowserEmulator be;
@BeforeClass
public void doBeforeClass() {
be = new BrowserEmulator();
}
@Test(description = "从车库进入超市")
public void step_1() {
}
@Test(description = "买牛奶", dependsOnMethods = "step_1")
public void step_2() {
}
@Test(description = "买水果", dependsOnMethods = "step_2")
public void step_3() {
}
@Test(description = "现金付款", dependsOnMethods = "step_3")
public void step_4() {
}
@Test(description = "开车离开", dependsOnMethods = "step_4")
public void step_5() {
}
@AfterClass(alwaysRun = true)
public void doAfterClass() {
be.quit();
}
}
  实战与效果
  由于对UI自动化及Jenkins轻车熟路,在极短的时间内就完成了一套简易的UI自动化持续集成,整套流程的执行时间严格控制在 15分钟 以内(可开启用例并发执行模式),并且每隔30分钟执行一次:
  工程代码构建 -> 测试环境部署 -> 执行UI自动化
  最初,一共只有10个左右的UI自动化用例,但已经基本覆盖了产品的核心功能点(没考虑各种流程的排列组合),正是这只有区区几个UI自动化用例的持续集成却发挥了不小的作用:
  不少的各种低级错误(低级,不解释!)在构建阶段被拦截,并以邮件形式知会开发团队;
  引起QA测试工作Block的严重问题(不一定是Bug,也可能是环境配置,脏数据,等)被及时发现;
  用例数量确实不多,大家都乐意在第一时间去处理失败用例。
  所以,正如古人云: 银子钞票,多多益善;持续集成,愈早愈好 ,还是尽早写几个UI自动化,配置持续集成跑起来吧,它能够发现和预防的问题比你预计的多得多!而且,只要这套持续集成跑起来了,往里面添砖加瓦就轻松得多了,不再会有畏惧感,信心也会逐步增强。
  现在,每次产品迭代结束之后,测试团队就会及时整理一份类似上述的表格,大家一起参详下,分工下,补充用例,逐步完善。
  一些结论
  S产品的UI自动化是 小而美 的典型:
  用例不多,维护/补充很及时;
  代码冗余度很小,结构清晰;
  运行稳定且快速,深合持续集成的快字诀。

posted @ 2013-12-12 11:13 顺其自然EVO 阅读(411) | 评论 (0)编辑 收藏

自动化测试技巧之图片验证

 想起图片验证,我就会想到我曾经的第一个自动化测试程序,那会被图片验证纠结了很久,最后也没有想通该怎么处理,直到最近,我终于想通了,可是却离开那个程序了。
  人生憾事其一,回头时发现自己曾做过的事那么浅薄,却沾沾自喜。
  ------------------------------------------------------------------------
  图片验证,这个不光指的是被测程序上的那些图片的验证,更可以引申为一些比较难以用常规方法来验证的功能的验证。
  先不去纠结到底怎么实现图片验证,来简单聊聊图片验证的一些实现。
  目前所有的图片共可以分为两大类,矢量图和位图。如果你要比较的是矢量图,那么,亲,可以找些资料一起来研究么,这块我不是很懂。目前的测试中,需要比较的图片90%以上都是位图,就算有些地方是矢量图,也是当成位图来比较的,所以目前我们先解决位图的问题。
  位图比较的基本原理是按位比较,因为位图的本质是一个矩阵(黑白图)或三个矩阵(RGB图),所以我们可以通过按位比较来判断两张图是否完全一样,这个也是最简单的了。当然,现在已经有了很多成熟的图形比较算法或工具,我们可以去网上找找。
  然后来说说利用图片来验证一些比较难以用常规方法来验证的功能。这个的核心思想是:一个软件,在相同的数据源、相同的显示环境、相同的操作下所显示的内容是完全一致的。
  先来举个例子。比如说画图板的绘图功能,如果要对这个进行自动化测试,那我们应该怎么做呢?无从下手么?因为我们不敢保证我们所绘制的图片是真的符合预期的,因为很难判断出来,并且很多前辈高人也说过,如果涉及图片比较多的最好放弃自动化。
  那么就真的没救了吗?
  再假设,画图板中的矩形选择区、线性选择区的对象是被自定义的,即不能被当前任一款测试工具所识别的,那我们还能实现自动化吗?
  其实,也有一条路是可以走通的。绘图功能的本质就是把特定线条或者区域中的像素点修改为对应的颜色,也就是说,如果两次的操作的图片、图片大小、绘图的轨迹完全一样的话,那得到的图片也应该是一样的。至于随机事件和各种组合的结果验证,亲,让他们一边玩去,测试脚本很忙。
  到这里,我们可以大致总结下:
  1. 对于一些常规方法验证不了的问题,我们可以采用这种方法进行验证。不过操作最好小于3步,以减少误差;
  2. 截图比较的区域要尽可能避免无关因素的干扰,如系统时间等;
  3. 如果有动态数据,则需要对截图范围内的图片相似度做一些规定,两个时刻的相似度低于80%,要么重新制定截图区域策略,要么寻求其他更有效的方法。
相关文章
自动化测试之控件点击

posted @ 2013-12-12 11:10 顺其自然EVO 阅读(324) | 评论 (0)编辑 收藏

IOS6.1单元测试持续集成实践

 最近项目测试需要,调研并实践了下IOS下单元测试工具和框架。目前比较流行的工具有xcode自带的OCUnit、GHUnit等,我选择的是GHUnit,因为相比OCUnit,GHUnit具有如下优势:
  1、开源框架
  2、支持重复测试、单一测试、集成测试。
  3、断言方法丰富
  4、支持持续集成
  5、测试类型多样(UI和Command Line)
  官方地址如下:http://gabriel.github.io/gh-unit/
  GitHub下载地址:https://github.com/gabriel/gh-unit
  下面我来讲下具体实现方法:
  一、测试准备
  1、xcode下载安装Command Line、测试的simulator
  2、若需要手机调试,按http://www.apkbus.com/android-465-1.html中方法设置
  3、下载GHUnit框架,并在Example/MyTestable-iOS中提取GHUnitiOS.framework
  二、新建项目
  1.首先,创建一个名为 GHUnitTest 的Empty Application,注意:不要选中 Include Unit Tests和 Use Core Data
  2.添加新的 test target,点击右侧的 Add Target,新增一个名为Test 的 Empty Application,让其附属于GHUnitTest,注意:不要选中 Include Unit Tests 和 Use Core Data
  3.向 Test添加 GHUnitIOS Framework,右击工程中的 Frameworks,选中 Add Files to...菜单,选取 GHUnitIOS.framework,targets 要选择Test
  4.设置 Test的 Build Settings:在 Other Linker Flags 中增加两个 flag: -ObjC 和 -all_load
  5.删除 Test下的 AppDelegate.h 和 AppDelegate.m 两个文件,注意不是GHUnitTest下
  6.选择编译目标 Test>iPhone 6.1Simulator,编译运行,应该能得到如下效果。目前我们还没有编写任何实际测试,所以列表为空。
  三、编写测试代码
  1.向 Test 工程中添加的 Objective C class测试类,继承于GHTestCase,并编写对应的测试方法,以下是一个AccessTest.m完整的测试类
#import "Test-Prefix.pch"
@interface AccessTest : GHTestCase
@end
@implementation AccessTest
- (void)setUpClass{
GHTestLog(@"Test Starts");
}
- (void)tearDownClass{
GHTestLog(@"Test ends");
}
-(void)tearDown{
[NSThread sleepForTimeInterval:1];
}
- (void)testOne{
GHTestLog(@"app starts first");
}
-(void)testTwo{    [
GHTestLog(@"app starts sencond");
}
@end


 2.修改main.m代码,以支持测试结果导出及持续集成
int main(int argc, char *argv[])
{
int retval;
@autoreleasepool {
if(getenv("GHUNIT_CLT")){
retval = [GHTestRunner run];
}else{
retval = UIApplicationMain(argc, argv, nil, @"GHUnitIOSAppDelegate");
}
}
return retval;
}
  四、编译运行
  1.编译运行后如图:
  2.点击run,每个case都会跑过去结束后可点击任意一个case,可显示成功的日志或者失败的内容,如图
五、持续集成
  方式一:命令行测试
  1.设置build periodically
  2.添加shell脚本
  GHUNIT_CLT=1 WRITE_JUNIT_XML=1 GHUNIT_AUTORUN=1 xcodebuild -target Test -sdk iphonesimulator6.1 -configuration Debug build
  3.添加Public JUnit test result report:GHUnitTest/build/test-results/*.xml,其他设置看需求,保存
  4.点击立即构建,控制会打出测试的内容,具体内容就不截图了,看最后的结果吧
  5.test-result如图
  方式二:UI测试
  与一的区别在于shell脚本如下:
instruments -t /Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate appName -e UIASCRIPT absolute_path_to_the_test_file

posted @ 2013-12-12 10:39 顺其自然EVO 阅读(403) | 评论 (0)编辑 收藏

XSS攻击技术详解

 一、背景知识
  1、什么是XSS攻击
  XSS攻击:跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆。故将跨站脚本攻击缩写为XSS。XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。比如这些代码包括HTML代码和客户端脚本。攻击者利用XSS漏洞旁路掉访问控制--例如同源策略(same origin policy)。这种类型的漏洞由于被骇客用来编写危害性更大的phishing攻击而变得广为人知。对于跨站脚本攻击,黑客界共识是:跨站脚本攻击是新型的"缓冲区溢出攻击",而JavaScript是新型的"ShellCode"。
  XSS攻击的危害包括:
  ◆盗取各类用户帐号,如机器登录帐号、用户网银帐号、各类管理员帐号;
  ◆控制企业数据,包括读取、篡改、添加、删除企业敏感数据的能力;
  ◆盗窃企业重要的具有商业价值的资料;
  ◆非法转账;
  ◆强制发送电子邮件;
  ◆网站挂马;
  ◆控制受害者机器向其它网站发起攻击。
  2、XSS漏洞的分类
  XSS漏洞按照攻击利用手法的不同,有以下三种类型:
  类型A,本地利用漏洞,这种漏洞存在于页面中客户端脚本自身。其攻击过程如下所示:
  Alice给Bob发送一个恶意构造了Web的URL。
  Bob点击并查看了这个URL。
  恶意页面中的JavaScript打开一个具有漏洞的HTML页面并将其安装在Bob电脑上。
  具有漏洞的HTML页面包含了在Bob电脑本地域执行的JavaScript。
  Alice的恶意脚本可以在Bob的电脑上执行Bob所持有的权限下的命令。
  类型B,反射式漏洞,这种漏洞和类型A有些类似,不同的是Web客户端使用Server端脚本生成页面为用户提供数据时,如果未经验证的用户数据被包含在页面中而未经HTML实体编码,客户端代码便能够注入到动态页面中。其攻击过程如下:
  Alice经常浏览某个网站,此网站为Bob所拥有。Bob的站点运行Alice使用用户名/密码进行登录,并存储敏感信息(比如银行帐户信息)。
  Charly发现Bob的站点包含反射性的XSS漏洞。
  Charly编写一个利用漏洞的URL,并将其冒充为来自Bob的邮件发送给Alice。
  Alice在登录到Bob的站点后,浏览Charly提供的URL。
  嵌入到URL中的恶意脚本在Alice的浏览器中执行,就像它直接来自Bob的服务器一样。此脚本盗窃敏感信息(授权、信用卡、帐号信息等)然后在Alice完全不知情的情况下将这些信息发送到Charly的Web站点。
  类型C,存储式漏洞,该类型是应用最为广泛而且有可能影响到Web服务器自身安全的漏洞,骇客将攻击脚本上传到Web服务器上,使得所有访问该页面的用户都面临信息泄漏的可能,其中也包括了Web服务器的管理员。其攻击过程如下:
  Bob拥有一个Web站点,该站点允许用户发布信息/浏览已发布的信息。
  Charly注意到Bob的站点具有类型C的XXS漏洞。
  Charly发布一个热点信息,吸引其它用户纷纷阅读。
  Bob或者是任何的其他人如Alice浏览该信息,其会话cookies或者其它信息将被Charly盗走。
  类型A直接威胁用户个体,而类型B和类型C所威胁的对象都是企业级Web应用,目前天清Web应用安全网关产品所能防范的XSS攻击包括类型B和类型C。

posted @ 2013-12-11 11:10 顺其自然EVO 阅读(1504) | 评论 (0)编辑 收藏

自动化测试开展的条件

 之前有朋友问过这个问题:什么样的项目适合做自动化测试?这个问题也是很多面试自动化测试工程师时经常被问到的一个问题,下面我就简单说下自己的看法:
  首先,为什么要进行自动化测试?
  这个也是个老生常谈的问题了。为什么要进行自动化测试呢?最根本的原因就是测试员们花费太多的时间去重复执行一些测试而导致项目进度延误,并且,由于大量的重复执行,使得很多测试员在后期的执行测试中往往会忽略很多东西,从而导致测试的整体质量和水平下降。自动化测试就是把我们需要花费大量时间去重复执行的测试操作用脚本实现,从而让测试人员从枯燥的重复执行中解放出来去做一些其他事情。
  看到上面的说明,我想我们现在对自动化测试应该有个大致的概念了。既然概念有了,那继续回到最初的问题:什么样的项目适合做自动化测试?
  在回答之前,我们先来看几个场景,用场景来说明下什么样的项目适合做自动化测试(为了方便场景中均未开发完成后测试正式介入执行;场景的时间是自己随便写的,大家别太纠结了)。
  Scenario 1:
  项目A为一个长期性的项目。每10个月有个大版本,对程序5%~30%的布局进行更改,期间可能会增删一些功能;每1个月有个小版本,修改量很小,一般为页面元素和资源的替换,偶尔会有一些小功能修改。
  Scenario 2:
  项目B为一个周期比较长的项目,持续时间为2年。项目为持续集成模式,预计开始集成起始时间为6月到次年3月,交付时间为次年12月,一次性交付。
  Scenario 3:
  项目C为一个中期项目,持续时间6个月,预计测试时间2月。
  Scenario 4:
  D为同类型的若干项目,功能相似,页面元素资源不一致
  Scenario 5:
  项目E为一个短期项目,时间一个月,测试时间2~3星期。
  在以上的项目中,项目A、B是最适合做自动化测试的,首先,测试时间足够充分;其次,测试为分期执行,中间有很多需要重复执行的测试部分。项目C如果技术能力足够,也是可以实现自动化测试。项目D需要根据测试时间和技术能力来决定是否需要做自动化测试。项目E就算了,时间太赶,而且只有一期,投入跟产出不成正比。
  综上所述,我们可以大致得出以下必要条件:项目测试时间较长或项目周期较长,需要大量重复执行测试的情况下。
  项目角度说完了,下面从技术角度来说下。
  自动化测试主要是通过代码或脚本模拟手工测试的执行,所以要实现自动化测试必须满足以下几点:
  1. 测试操作可明确用脚本实现;
  2. 操作结果明确可以验证;
  不过现在测试技术发展这么快,很难说哪些一定不能用技术手段实现,所以,技术这块我还是silence吧,毕竟,我也才是个新人。

posted @ 2013-12-11 11:04 顺其自然EVO 阅读(221) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 171 172 173 174 175 176 177 178 179 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜