1. 概述
本文针对
QTP提供的运行时错误处理机制进行详细描述。内容包括各种错误处理方式的功能的介绍,各种机制的作用范围的分析,以及错误处理机制同时作用时的优先级别的分析。
2. 三种错误处理方式介绍
Quick Test Professional 中有三种错误处理机制,在这里定义为:
全局错误响应: Test Setting 中Run 设置
VBScript On Error 错误处理: On Error Resume Next
错误恢复场景: Recovery Scenarios
2.1 全局错误响应
全局错误响应在Test Setting 的Run 选项中进行设置:【File】—>【Settings】—>【Run】—>【When error occurs during run session】
QTP四种全局错误响应设置选项如下:
pop up message box: Quick Test 在出现错误时显示一个错误消息对话框
process to next action iteration:Quick Test 出现错误时继续下一个Action 循环
stop run: Quick Test 在出现错误时停止
测试 process to next step:Quick Test 在出现错误时跳过错误语句,继续下一步骤
全局错误响应是QTP 的系统缺省错误处理,就是说当没有使用其他错误处理方式时,系统会自动调用在这里指定的方式进行错误处理
2.2 VBScript 的On Error 错误处理方式
由于QTP 使用了VBScript 作为脚本语言,自然地,VBScrip 的错误处理方式在QTP 中都适用。在VBScript中,与错误处理相关的三者为:
On Error Resume Next 语句
On Error GoTo 0 语句
Err 对象
2.2.1 On Error Resume Next
一旦这个语句已被处理,脚本引擎将继续运行后面的程序,而不理会已经发现的任何错误。
2.2.2 On Error GoTo 0
使用On Error Goto 0 语句恢复缺省的错误处理行为。在运行这个语句后,发生的运行期错误将导致缺省错误处理。在QTP 中,缺省错误处理就是全局错误响应。
2.2.3 Err 对象
Err 对象重要的属性有三个:Number, Source, Description,分别是错误号,错误来源,错误描述.Err 对象会在运行时错误发生的时候赋予新的值,旧的值会被舍弃。Err 对象不会受到函数调用的影响,它完全是全局的。Err 对象可以用Clear 方法清空,任何时候调用On Error Resume Next 或者On Error GoTo 0 的时候,都会清空Err 对象。
2.3 恢复场景Recovery Scenarios
Resources_Recovery Scenarios Manager…可以创建恢复场景。具体请参考帮助文档,这里只是说明对于运行错误的处理。在恢复场景中触发事件TriggerEvent 中的一个选项是Test Run Error,表示在运行过程中遇到错误时所采取的恢复处理方式。我们看一看下面对哪几种错误进行恢复处理,即错误恢复的触发条件:
Any Error Item in list or menu is not unique Item in list or menu not found More than one object responds to the physical description Object is disable Object not found Object not visible |
恢复处理(Recovery Operation)有以下几种方式:
Keyboard or mouse operation Close application process Function call |
从恢复场景的触发条件可以看出,所有错误处理都是针对测试对象发生的错误而言的(Any Error 指列表中所有的其他错误类型),它并不处理被零除、非法赋值,内存分配错误等等VBScript 的运行时错误。这是和其它两种错误处理方式不同之处。为了说明这一点,我们不妨再看看Recovery Operation 中OperationType 如果选择Function Call,函数原型定义如下所示,所有的函数参数都必须包含有被测对象,触发函数必须遵循原型定义,否则函数无法执行。
再来看错误恢复的后处理方式(Post-Recovery Test Run Options)有以下几种:
Repeat current step and continue Proceed to next step Proceed to next action or component iteration Proceed to next test iteration Restart current test run Stop the test run |
我们看到Proceed to next step/ Proceed to next action or component iteration/ Stop the test run这三种方式在全局错误处响应理方式中也是存在的,Proceed to next step 这种方式更是和On ErrorResume Next 处理方式也是相同的。稍后会就此作进一步叙述。
3. 三种错误处理方式的作用域
本节阐述三种错误处理方式的作用范围,为了方便比较说明,这里假设三种错误处理方式为:
· 全局错误响应: Process to next step
· VBScript 错误处理: On Error Resume Next
· 错误恢复后处理方式: Proceed to next step
因为这三种处理方式效果一样,以后没有特殊说明,都采用以上设置,可以方便比较
3.1 全局错误响应
原则: 对该test 中的所有Action 均生效。也就是说在Action 里没有其他错误处理方式的时候,脚本运行过程中遇到的错误都会按照全局错误响应的设置进行处理
3.1.1 对 Action 中直接定义的函数:
对所有函数生效。如下例,如果全局错误响应设置成process to next step 函数testA 中的被零除语句 i=1/0 会跳过,三个msgbox 均可以执行。错误代码会逐级返回。
3.1.2 对用ExecuteFile 引入的函数:
与Action 中直接定义的函数处理方式完全一样
3.1.3 对于Function Library 中定义的函数
与Action 中直接定义的函数处理方式基本一致,对所有函数生效。但不同的是,对于错误代码,只能在Function Library 中传递,而不会传递到Action 中来。
3.1.4 对于Action 的嵌套调用
如之前所述,对Test 中的所有Action 均生效,自然嵌套调用的Action 也不例外。同Function Library一样,错误码只能在Action 内部传递,也就是说被调用Action 中产生的错误码不能传递到调用Action 中去。
3.2 VBScript 的On Error 错误处理方式
原则: 只对语句所在函数或Action 中该语句之后的代码生效,对子函数,调用Action 均无效。
3.2.1 对 Action 中直接定义的函数:
函数中错误抛出,不会继续执行错误代码。一个错误在函数/子程序中出现时,如果没有运行On Error Resume Next 语句,那么错误将被交给调用它的环境,这个过程一直重复到找到运行On Error Resume Next 语句的环境继续运行,或者找到缺省的脚本错误处理器。
3.2.2 对用ExecuteFile 引入的函数:
与对Action 中直接定义的函数的处理完全一致
3.2.3 对于Function Library 中定义的函数
Function Library 中定义的函数遇到错误抛出,执行系统缺省错误处理
3.2.4 对于Action 的嵌套调用
被调用Action 中的语句遇到错误抛出,执行系统缺省错误处理
3.3 错误恢复场景 Recovery Scenarios
原则: 任何函数调用和Action 中,只要满足触发条件,就会触发错误恢复处理和后处理
前面讲过,错误恢复场景的触发条件是以下6 种
Item in list or menu is not unique Item in list or menu not found More than one object responds to the physical description Object is disable Object not found Object not visible |
对于VBScript 的其他运行时错误,并不会触发错误恢复场景,当然也不存在错误恢复处理及后处理。但一旦在任何函数或者action 中满足触发条件,都会进行错误恢复处理及后处理,也就是说,其作用范围为所有Action 和函数/子程序中。当错误恢复完成后(包括错误处理及后处理),错误会被清0。如果错误恢复的后处理方式为Proceed to next step,处理完成后再次碰到VBScript 错误时如何处理呢?如新产生错误并不能满足触发条件,则原错误恢复中设置后处理方式不会继续生效,此时会遵循缺省错误响应或其他指定的错误处理。值得注意的是,如果错误恢复的后处理方式为Repeat current step and continue 或者Restart currenttest run,再次运行到该语句仍重复同样的错误,可能会导致死循环。
4. 三种错误处理方式的优先级别
错误恢复场景中的错误处理优先级最高 (Recovery Operation)
On Error Resume Next 优先级次之
错误恢复场景中的后处理方式优先级别再次之 (Post-Recovery Test Run Options)
缺省错误响应优先级别最低
也就是说,只要满足恢复场景的触发条件,就会进行错误恢复处理(Recovery Operation)。但是其后处理方式的优先级别和错误恢复处理的优先级别是不同的。如果触发错误恢复处理的语句之前有On ErrorResume Next,就不会执行错误恢复后处理中所定义的处理方式。当没有找到以上任何错误处理方式的定义时,才执行缺省错误处理。
注意: On Error GoTo 0 其实就是指采用缺省错误处理,不可和On Error Resume Next 处理的优先级别等同起来。
5. 自动化框架测试与实践
5.1错误处理模式三种测试设计技术
为了解决传统错误处理模式的不足,现在错误处理的技术往往采取如下的手段。
图片处理技术
伪视频技术
视频技术
5.1.1图片处理技术
图片处理技术主要是基于原先的图片截图上进行扩展的,只要的目的是采取简便的方式来展示错误信息的显示,一般的操作过程如下:
出现错误的时候开始截屏
记录错误的控件的位置
在图片上标示控件
在控件附近标示错误的信息
这样设计的目的就是希望方便测试人员或者开发人员能够很方便的了解导致程序出错的过程,接下来我会针对图片处理技术来进行实践。
5.1.2伪视频技术
伪视频的技术是为了展现因为测试过程而导致出现错误的现象。它所实现的过程是采取对每个操作过程的截屏,然后生成Gif格式的文件,类似一个动画的效果。伪视频的技术操作过程如下:
自动化测试脚本在运行之初,初始化一段缓冲区来保存即将捕获的截图
自动化测试脚本按照顺序进行逻辑顺序执行
“伪视频程序”也在按照一定的频率进行截图处理,并把图片保存在缓冲区
如果缓冲区达到最大容量,尽早的截图将会被新的图片代替
一旦错误出现,自动化脚本通知“伪视频程序”,“伪视频程序”将手机缓冲区的截图,然后形成动态图片
脚本运行停止
5.1.3视频技术
这是个真正的屏幕录制技术,采取的是自动化测试执行与屏幕录制平台操作的过程, 来录制自动化测试的一个过程。但是这个技术最大的问题在于,需要的空间比较大,如果空间小的话,就将会导致出错的过程难以有效地展现.
引言
借年底盛宴品鉴之风,继续抒我Hadoop之情,本篇
文章介绍如何对Hadoop的MapReduce进行
单元测试。MapReduce的开发周期差不多是这样:编写mapper和reducer、编译、打包、提交作业和结果检索等,这个过程比较繁琐,一旦提交到分布式环境出了问题要定位调试,重复这样的过程实在无趣,因此先对MapReduce做单元测试,消除明显的代码bug尤为必要。
MRUnit简介
MRUnit是一款由Couldera公司开发的专门针对Hadoop中编写MapReduce单元测试的框架。可以用MapDriver单独测试Map,用ReduceDriver单独测试Reduce,用MapReduceDriver测试MapReduce作业。
实战
我们将利用MRUnit对本系列上篇文章MapReduce基本编程中的字数统计功能进行单元测试。
· 加入MRUnit依赖
<dependency> <groupId>com.cloudera.hadoop</groupId> <artifactId>hadoop-mrunit</artifactId> <version>0.20.2-320</version> <scope>test</scope> </dependency> |
· 单独测试Map
public class WordCountMapperTest { private Mappermapper; private MapDriverdriver; @Before public voidinit(){ mapper = newWordCountMapper(); driver = newMapDriver(mapper); } @Test public voidtest() throws IOException{ String line ="Taobao is a great website"; driver.withInput(null,newText(line)) .withOutput(newText("Taobao"),new IntWritable(1)) .withOutput(newText("is"), new IntWritable(1)) .withOutput(newText("a"), new IntWritable(1)) .withOutput(newText("great"), new IntWritable(1)) .withOutput(newText("website"), new IntWritable(1)) .runTest(); } } |
上面的例子通过MapDriver的withInput和withOutput组织map函数的输入键值和期待的输出键值,通过runTest方法运行作业,测试Map函数。测试运行通过。
· 单独测试Reduce
public class WordCountReducerTest { private Reducerreducer; privateReduceDriver driver; @Before public voidinit(){ reducer = newWordCountReducer(); driver = newReduceDriver(reducer); } @Test public voidtest() throws IOException{ String key ="taobao"; List values =new ArrayList(); values.add(newIntWritable(2)); values.add(newIntWritable(3)); driver.withInput(new Text("taobao"), values) .withOutput(new Text("taobao"), new IntWritable(5)) .runTest(); } } |
\
上面的例子的测试Map函数的写法类似,测试reduce函数, 因为reduce函数实现相加功能,因此我们假设输入为<taobao,[2,3]>,
则期待结果应该为<taobao,5>.测试运行通过。
· 测试MapReduce
public class WordCountTest { private Mapper mapper; private Reducer reducer; private MapReduceDriver driver; @Before public void init(){ mapper = new WordCountMapper(); reducer = new WordCountReducer(); driver = new MapReduceDriver(mapper,reducer); } @Test public void test() throws RuntimeException, IOException{ String line = "Taobao is a great website, is it not?"; driver.withInput("",new Text(line)) .withOutput(new Text("Taobao"),new IntWritable(1)) .withOutput(new Text("a"),new IntWritable(1)) .withOutput(new Text("great"),new IntWritable(1)) .withOutput(new Text("is"),new IntWritable(2)) .withOutput(new Text("it"),new IntWritable(1)) .withOutput(new Text("not"),new IntWritable(1)) .withOutput(new Text("website"),new IntWritable(1)) .runTest(); } } |
这次我们测试MapReduce的作业,通过MapReduceDriver的withInput构造map函数的输入键值,通过withOutput构造reduce函数的输出键值。来测试这个字数统计功能,这次运行测试时抛出了异常,测试没有通过但没有详细junit异常信息,在控制台显示
2010-11-5 11:14:08org.apache.hadoop.mrunit.TestDriver lookupExpectedValue严重:Received unexpectedoutput (not?, 1)
2010-11-5 11:14:08org.apache.hadoop.mrunit.TestDriver lookupExpectedValue严重: Received unexpectedoutput (website,, 1)
2010-11-5 11:14:08org.apache.hadoop.mrunit.TestDriver validate严重:Missing expected output (not, 1) atposition 5
2010-11-5 11:14:08org.apache.hadoop.mrunit.TestDriver validate严重:Missing expected output (website, 1)at position 6
看样子是那里出了问题,不过看控制台日志不是很直观,因此我们修改测试代码,不调用runTest方法,而是调用run方法获取输出结果,再跟期待结果相比较,mrunit提供了org.apache.hadoop.mrunit.testutil.ExtendedAssert.assertListEquals辅助类来断言输出结果。
重构后的测试代码
@Test public void test() throws RuntimeException, IOException{ String line = "Taobao is a great website, is it not?"; List<Pair> out = null; out = driver.withInput("",new Text(line)).run(); List<Pair> expected = new ArrayList<Pair>(); expected.add(new Pair(new Text("Taobao"),new IntWritable(1))); expected.add(new Pair(new Text("a"),new IntWritable(1))); expected.add(new Pair(new Text("great"),new IntWritable(1))); expected.add(new Pair(new Text("is"),new IntWritable(2))); expected.add(new Pair(new Text("it"),new IntWritable(1))); expected.add(new Pair(new Text("not"),new IntWritable(1))); expected.add(new Pair(new Text("website"),new IntWritable(1))); assertListEquals(expected, out); } |
再次运行,测试不通过,但有了明确的断言信息,
java.lang.AssertionError:Expected element (not, 1) at index 5 != actual element (not?, 1)
断言显示实际输出的结果为"not?"不是我们期待的"not",为什么?检查Map函数,发现程序以空格为分隔符未考虑到标点符号的情况,哈哈,发现一个bug,赶紧修改吧。这个问题也反映了单元测试的重要性,想想看,如果是一个更加复杂的运算,不做单元测试直接放到分布式集群中去运行,当结果不符时就没这么容易定位出问题了。
小结
用MRUnit做单元测试可以归纳为以下几点:用MapDriver单独测试Map,用ReduceDriver单独测试Reduce,用MapReduceDriver测试MapReduce作业;不建议调用runTest方法,建议调用run方法获取输出结果,再跟期待结果相比较;对结果的断言可以借助org.apache.hadoop.mrunit.testutil.ExtendedAssert.assertListEquals。
如果你能坚持看到这里,我非常高兴,但我打赌,你肯定对前面大片的代码匆匆一瞥而过,这也正常,不是每个人都对测试实战的代码感兴趣(或在具体需要时才感兴趣),为了感谢你的关注,我再分享一个小秘密:本篇讲的不仅仅是如何对MapReduce做单元测试,通过本篇测试代码的阅读,你可以更加深刻的理解MapReduce的原理(通过测试代码的输入和预期结果,你可以更加清楚地知道map、reduce究竟输入、输出了什么,对结果的排序在何处进行等细节)。
单元测试很必要,可以较早较容易地发现定位问题,但只有单元测试是不够的,我们需要对MapReduce进行集成测试,在运行集成测试之前,需要掌握如何将MapReduce 作业在hadoop集群中运行起来,本系列后面的文章将介绍这部分内容。
测试页面见:test/load-eff-test.html
为了测试结果更显客观,我选择了第三方类库的装载测试:
'com.yahoo.yui.*', 'net.conio.prototype.*', 'net.fckeditor.*', 'org.jquery.*', 'us.aculo.script.*' |
共22个脚本文件(对于JSI来说还有诺干包定义文件)。
FF2: 标记导入时间(原始方式):469,469,1047,484,484,437,469,484 同步导入时间:469,453,484,437,469,453 延迟导入时间:921,765,891,906,953,906,922 异步导入时间:859,1093,1141,1031,1641,1125,1078,1093,1157,1141 IE7: 标记导入时间:343,297,297,344,328,328 同步导入时间:281,250,235,235,234,234,250,265 延迟导入时间:922,422,406,391,391,391,407,391 异步导入时间:625,672,672,703,703,672,703,704,688 |
运行时间测试
测试脚本管理后对新能的影响,影响因素有:全局变量和局部变量的查找时间差异,eval的脚本和script标记直接插入的脚本的可能差异。(这个测试不具有普遍性,这里我主要是测试了一下浏览器对局部变量的访问速度【JSI里面访问变量都是装载单元内的局部变量】,所以故意测试了大量局部变量访问的操作)
测试页面见:test/runtime-eff-test.html
FF2: jsiTime: 845, 927, 598, 687, 764, scriptTime: 1432, 950, 1305, 1278, 1219, evalTime: 1644, 1373, 1322, 1186, 1360, execTime: 0 dscriptTime: 1432, 950, 1305, 1278, 1219, IE7: jsiTime: 295, 205, 157, 315, 156, 142, 375, 328, 172, 172, scriptTime: 172, 172, 189, 140, 251, 187, 217, 203, 172, 234, evalTime: 236, 249, 139, 172, 281, 171, 172, 108, 436, 359, execTime: 219, 234, 314, 157, 220, 266, 204, 234, 187, 95, dscriptTime: 187, 265, 294, 326, 187, 328, 141, 221, 127, 249, |
上面的基数太小,随机误差太大,调整原始数据从新测试一遍jsiTime和scriptTime
jsiTime: 576, 658, 688, 703, 611, 608, scriptTime: 706, 608, 562, 547, 655, 657, |
总结:
JSI的装载性能表现不错,完全不必计较。
托管代码的运行性能也没有太大区别,不过,因为。JSI托管脚本使用的变量基本都是装载单元内的局部变量(本地声明变量,或者外部依赖的引用或值拷贝),所以,对于FF这类局部变量比全局变量访问速度快不少的解释引擎,JSI托管脚本可以达到更好的运行效率。
单元测试是被测的函数都只作用于其所属的类,
接口测试是测试多个类/模块间的相互作用,即目标是被测函数如何被调用以及调用后会对外产生什么结果。
既然是专注于模块间作用,那么可测点就是public的接口,其可分为:
主动调用型是指被测函数需要主动调用,以测试其结果或影响。故测试目标有两类:
1.对“获取型”的接口是在不同的时机执行获取动作,测试返回/输出值是否符合预期
2.对“操作型”的接口是调用后会对其它类和接口产生影响,测试别的接口行为是否符合预期。也就是纯操作型接口本身可以不测,而是作为其它案例的触发方法。纯操作型函数大多数是返回值为void的。
被动调用型是指添加子类或实现接口来覆盖原函数以获取被调用的时机,测试目标为是否有触发、触发次数、参数的正确性等。
接口测试是不会为测试而修改被测模块的。
PDO是一个"
数据库访问抽象层",作用是统一各种数据库的访问接口,与mysql和mysqli的函数库相比,PDO让跨数据库的使用更具有亲和力:与ADODB和MDB2相比,PDO更高效.
目前而言,实现"数据库抽象层"任重而道远,使用PDO这样的"数据库访问抽象层"是一个不错的选择.
一.PDO基本函数库
PDO->beginTransaction() 标明回滚起始点
PDO->commit 标明回滚结束点,并执行
SQL PDO->__construct 建立一个PDO链接数据库的实例
PDO->errorCode 获取错误码
PDO->errorInfo 获取错误的信息
PDO->exec 处理一条SQL语句,并返回所影响的条目数
PDO->getAttribute 获取一个“数据库连接对象”的属性
PDO->getAvailableDrivers 获取有效的PDO驱动器名称
PDO->lastInsertId 获取写入的最后一条数据的主键值
PDO->prepare 生成一个“查询对象”
PDO->query 处理一条SQL语句,并返回一个“PDOStatement”
PDO->quote 为某个SQL中的字符串添加引号
PDO->rollBack 执行回滚
PDO->setAttribute 为一个“数据库连接对象”设定属性
二.PDO对mysql数据库相关操作
修改php.ini配置文件
1、找到与pdo相关的所有扩展
2、去掉pdo扩展前所有分号
3、重启apache服务
查询操作,select查询语句
一种方式:
$url = "mysql:host=主机地址;dbname=库名"; //连接数据库的参数 $user = "用户名"; $pwd = "密码"; $conn = new PDO($url,$user,$pwd); //连接数据库 $conn->query("set names utf8"); //设置编码 $st = $conn->query("select查询语句"); //执行select查询语句,返回数据库操纵对象statement $rs = $st->fetchAll(); //获得结果集,结果集就是一个二维数组 //显示所有记录 foreach($rs as $value) { echo $value["字段名|列序号"]; } //关闭数据库 unset($rs); unset($st); unset($conn); |
二种方式:
$url = "mysql:host=主机地址;dbname=库名"; $user = "用户名"; $pwd = "密码"; $conn = new PDO($url,$user,$pwd); $conn->query("set names utf8"); $st = $conn->prepare("select语句");//指定 $st->execute();//执行 $rs = $st->fetchAll(); foreach($rs as $value) { echo $value["字段名|列序号"]; } unset($rs); unset($st); unset($conn); |
mysql增、删、改操作,insert|update|delete语句
一种方式:
$url = "mysql:host=主机地址;dbname=库名"; $user = "用户名"; $pwd = "密码"; $conn = new PDO($url,$user,$pwd); $conn->query("set names utf8"); $row = $conn->exec("insert|update|delete语句"); unset($conn); |
二种方式:
$url = "mysql:host=主机地址;dbname=库名"; $user = "用户名"; $pwd = "密码"; $conn = new PDO($url,$user,$pwd); $conn->query("set names utf8"); $st = $conn->prepare("insert|update|delete语句"); //指定要执行的sql语句, 指定where Id=:a ,参数名:a $st->bindParam(":名",$变量); //bind绑定 paramter参数:为sql语句中的:a 绑定变量$id $result = $st->execute(); //执行prepare所指定的sql语句 unset($st); unset($conn); |
Robotium是一个开源的androidUI自动
测试工具,这里不做介绍,可以见:http://code.google.com/p/robotium/
Orange:
Orange 是我们自己开发的运行在PC端的(只支持windows),用于组织
测试用例、自动重签名apk文件、以及操作模拟器、启动运行测试用例、用 例crash以及失败重跑、测试结果收集等功能的一个工具,通过简单的配置,既可以全自动全SDK版本回归运行robotium编写的测试用例。
为什么开发Orange?
为什么要自己开发一个Orange工具,而不直接使用Robotium编写的测试代码来运行测试用例?
我们在Robotium测试用例运行的过程中发现了以下的一些问题:
1、测试用例运行过程中有时候会直接Crash,导致所有测试用例停止,同时收集不到测试结果
2、用例在模拟器上运行,用例经常会出现失败的情况, 但是你再次运行的时候可能就用成功了,存在不稳定性
另外我们希望有下面的功能:
1、我们希望用户编写好以及robotium的测试用例后,能够方便的全自动全SDK版本回归运行测试用例
2、我们希望用户能够方便的指定当前运行哪些测试用例
3、我们希望能够得到一个直观的运行结果报告
当前这里最重要的是在使用Robotium发现的问题,如果你的用例在运行时crash了,导致什么结果也收集不到, 如果你的自动测试用例稳定性太低,经常出现大量的失败用例,那么用户如何来相信你的测试结果,大量失败用例你去分析失败原因也是一个非常耗时的过程,如果 不能很好的解决稳定性的问题,搭建的自动测试框架失败率会大大的提高。
Orange使用步骤:
为了更好的生成测试结果报告,我们这里使用了一个开源的软件android-junit-report,官方地址:https://github.com/jsankey/android-junit-report ,该软件继承重写了
android自带的InstrumentationTestRunner,会自动把测试结果生成到xml文件中。
所以基于Robotium编写测试用例的时候需要额外的进行下面的两部操作:
1、从https://github.com/jsankey/android-junit-report下载jar包,同时加入到测试用例程序中
2、修改测试程序的AndroidMainifest.xml文件,在文件中加入以下配置
<instrumentation android:targetPackage="com.netease.pris(被测应用的包名)" android:name="com.zutubi.android.junitreport.JUnitReportTestRunner" /
接下去就可以开始编写测试用例了,测试用例编写完成后,
1、获取到被测应用的apk文件,以及你的测试程序的apk文件
2、填写orange框架的配置文件,有一些指定的必填配置项
该文件主要是填写需要测试的sdk版本、被测应用的apk位置、被测应用的主包名、测试程序的apk位置、测试结果收件人列表、测试用例的xml文件地址
config.properties
#填写需要测试的android sdk版本,多个的话中间用逗号隔开,不允许为空
target=android-10,android-14
#
被测试应用存放的位置,windows下目录之间用"\\"来分割,不允许为空
apkpath=D:\\PRIS\\1.3.1\\pris_generic_1.3.1.apk
# 测试程序存放的位置,windows下目录之间用"\\"来分割,不允许为空
testapkpath=D:\\PRIS\\1.3.1\\PrisUIAutoTest.apk
#
测试程序的主包名,不允许为空 package=com.netease.pris
# 运行结果的收件人列表,多个用户用逗号分割,允许为空 maillists=***@163.com,***@163.com
# 测试用例的配置文件绝对路径,不允许为空 testCaseFile=D:\\TestCaseInfo.xml
3、编写指定需要运行的测试用例文件
这里类似于testng的方式来指定需要运行哪些测试用例,但是为了解决crash以及失败重跑的问题,只支持每个测试方法单独配置,而不支持只填写到测试类级别。
TestCaseInfo.xml
<!-- packageName 填写的是测试程序的主包名,必填项 -->
<classes packageName="com.netease.mobile.autotest">
<!--
指定需要运行的测试类,这些类是在测试程序中编写的,也会安装到模拟器中-->
<class name="com.netease.mobile.autotest.testing.PersonalDataTest" >
<methods>
<!--
这里定义需要运行的具体测试方法,这些都是必填项,如果不存在会报错-->
<include name="test01ShowPersonalData" /> <include name="test02EditPersonalData" /> </methods> </class> </classes> |
4、如何运行测试程序
orange框架会生成一个jar包,当你配置好这些选项后,并且拿到了orange.jar后,直接运行命令
java -jar Orange.jar D: \\config.properties
则orange框架会开始自动为测试应用以及被测应用重签名,按照配置项,创建、启动模拟器、安装apk文件、运行测试用例(crash自动重启模拟器,失败则自动重跑,当前最多跑三次),测试用例运行完成后收集测试结果,发送到指定的收件人。
Orange实现原理:
1.1 自动重签名APK文件
使用Robotium来测试应用的话,需要测试程序和被测程序的签名是一样的,但是一般被测程序都会有自己签名过,所以我们需要实现自动的删除被测程序以及测试程序的签名文件,然后使用统一的key对被测程序的测试程序进行签名。
实现原理:
1、删除apk文件中的META-INF文件夹
使用winrar工具的 winrar d ***.apk META-INF 命令可以实现在不解压apk文件的情况下删除压缩包内指定文件夹
所以这里需要先下载安装winrar工具,另外在电脑的环境变量中配置winrar的安装目录,保证在cmd命令中能够调用到winrar命令
(PS:这里原来是使用的java jar -xvf 命令先把apk解压缩,然后调用删除方法删除掉META-INF,最后使用jar -cvf压缩为apk文件,但是通过jar命令解压缩之后,重签名后的网易阅读apk会出现崩溃的情况,改成了通过winrar工具来实现)
2、使用JARSIGNER命令对apk文件使用统一的key进行重签名操作
3、使用zipalign命令对重签名后的文件进行优化
Orange框架如何使用bat脚本:
1、bat脚本和重签名的key在Orange的jar包中会保存一份
2、运行Orange时,复制bat文件、key文件以及测试应用和被测试应用到temp目录下
3、通过java方法的Process process = Runtime.getRuntime().exec( "cmd.exe /c start " + path); 执行bat文件
4、bat文件会自动把当前目录下的apk文件全部都进行重签名,重签名的apk文件保存到指定目录下
1.2 自动创建、启动、删除模拟器以及安装apk功能
需要在不同sdk版本的模拟器上实现全自动切换运行测试用例,所以需要涉及到创建、启动、删除模拟器以及安装apk文件等功能。
实现原理:
所有这些操作都使用了android自带的android命令来实现的
1、创建模拟器 android create avd --name OrangeAutoTest --target android-7 --force
通过java中调用以上命令实现创建模拟器的操作,如何判断创建模拟器的是否成功,以及失败的话错误信息呢?
Process process = Runtime.getRuntime().exec(cmd);来执行上面的cmd命令行
然后通过process.getInputStream() 和process.getErrorStream()来获取到相应的返回信息,如果有错误信息的话可以再getErrorStream中获取到
说明: 正确情况下getErrorStream能够获取到值的话则抛出异常说明命令执行是时候出错了,但是有些命令的话执行正常,但是getErrorStream也能获取到返回值,所以有些命令需要做特殊的处理。
2、启动模拟器 emulator -avd OrangeAutoTest -sdcard c:\sdcard.img
通过上面的命令启动模拟器,同时加载sdcard,sdcard可以通过mksdcard 20M c:\sdcard.img命令来创建
3、删除模拟器android delete avd -n OrangeAutoTest
4、安装APK adb wait-for-device install -r *.apk
1.3 Crash以及失败用例重跑功能
平时我们使用Robotium编写完一些测试用例的时候经常是直接通过Eclipse运行多个测试用例,或者通过junit编写一个testsuite把需要运行的测试用例加入到testsuite中一次运行多个测试用例。
但是在运行的过程中,我们会发现有时候应用会Crash,导致的结果是测试暂停,所有测试的结果都收集不到。
另外在运行的时候因为模拟器的一些不稳定性,可能会存在某一次用例运行失败的情况,但是再次运行用例就恢复正常了。
为了解决Crash的问题,以及失败用例重跑的问题,我们开发了运行在PC端Orange框架。
实现原理:
1、运行测试用例的时候通过Orange框架控制每次只运行一个测试用例,这样子如果这一个用例运行crash了或者失败的话,我可以再次启动应用重新运行,对其它用例不会有影响。
首先通过Robotium编写的需要运行的测试用例需要配置在xml文件中,按照类似TestNG配置文件的格式来指定这次运行哪些测试用例
<!-- packageName is required--> <classes packageName="com.netease.mobile.autotest"> <class name="com.netease.mobile.autotest.testing.LoginTest"> <methods> <include name="testLogin" /> <include name="testUnLogin" /> </methods> </class> </classes> |
这里要求配置的时候必须配置到具体的方法,我们运行的时候Orange框架会解析这个xml文件,最后组合成以下的一些命令来循环运行测试用例,每次运行一个
adb shell am instrument -e class com.netease.mobile.autotest.testing.LoginTest#testLogin -w com.netease.mobile.autotest/com.zutubi.android.junitreport.JUnitReportTestRunner
adb shell am instrument -e class com.netease.mobile.autotest.testing.LoginTest#testUnLogin -w com.netease.mobile.autotest/com.zutubi.android.junitreport.JUnitReportTestRunner
2、如何判断用例是运行Crash的?
我们还是通过Process process = Runtime.getRuntime().exec(cmd);方法来运行第一步生成的adb命令,然后通过 process.getInputStream();获取到流的返回值,如果返回值中包含"shortMsg=Process crashed"字符的话,说明这个用例运行crash了,那我们使用重试机制
再次运行这个测试用例,如果连续3次都是crash的话,则把crash的信息也写入到返回值中,保证了用例运行Crash的话也能够收集的错误信息。
3、如果判断用例运行失败?
这里使用了JUnitReportTestRunner的一个开源的插件(https://github.com/jsankey/android-junit-report),是重写了官方的InstrumentTestRunner,能够帮我们收集每个用例的执行情况。
每次用例执行结束后,会在模拟器或者真机的指定位置下生成一个Junit-report.xml文件,我们通过adb pull 命令把这个文件保存到PC端,然后Orange框架来解析这个文件,如果结果中包含failure或者error的话说明这个用例运行失败了,则重跑用 例。
如果不包含的话说明用例运行成功,则把刚才取出的xml文件存下来,后续所有用例运行完成整合为一个完整的xml结果文件。
4、如何实现用例重跑?
这个只需要实现一个方法的递归调用就可以,每次运行的时候会查看返回值以及解析xml的返回结果文件,如果存在异常则递归调用该方法,同时会定义一个运行次数的参数,如果大于这个次数的时候不管是够通过都会保存当前最后一次的运行结果。
//调用运行测试用例方法 result = e.run(testCase, testAppPackageName); // 如果出现timeout异常则再运行一次 if (result.equals(EmulatorHelper.timeOutException)) { //如果超出运行次数,则保存错误信息,否则递归运行 if(j > runCount){ addErrorToXml(root, testCase, "adb shell am timeoutexception", beginTime,"TimeOutException"); return result; }else{ return runTest(root, e, testCase, testAppPackageName,appPackageName, j); } |
1.4 测试结果收集功能
每次运行一个用例结束的时候,会把当前用例的运行结果加入到xml的节点中,所有用例运行结束后,xml文件会保存在PC端的指定位置下。
收集到的测试结果样式如下:
<?xml version="1.0" encoding="UTF-8"?> <testsuites> <testsuite name="com.netease.mobile.autotest.testing.LoginTest"> <testcase classname="com.netease.mobile.autotest.testing.LoginTest" name="testUnLogin" time="59.499"/> </testsuite> <testsuite name="com.netease.mobile.autotest.testing.SubscribeTest"> <testcase classname="com.netease.mobile.autotest.testing.SubscribeTest" name="testAdd2" time="74.099"> <failure message="junit.framework.AssertionFailedError: Button with the text: 取消订阅 is not found!" type="junit.framework.AssertionFailedError">junit.framework.AssertionFailedError: Button with the text: 取消订阅 is not found! at com.jayway.android.robotium.solo.Clicker.clickOn(Clicker.java:324) at com.jayway.android.robotium.solo.Solo.clickOnButton(Solo.java:684) at com.netease.mobile.autotest.common.PrisOperation.addSubscribe(PrisOperation.java:91) at com.netease.mobile.autotest.testing.SubscribeTest.testAdd2(SubscribeTest.java:126)</failure> </testcase> </testsuite> </testsuites> |
1.5 测试报告生成功能
首先会获取到收集的测试结果xml文件,然后解析xml同时加入一些css样式,生成html文件,邮件中发送的正文内容就是html文件内容。
这里就是一个解析xml以及样式处理的过程。
问题1:用需、软需文档不够完整,需求不够明确,功能细节描述不足。
解决:
需求维护:Jira上的产品建议、运维反馈的产品建议。
需求文档维护。(系统的主要功能、流程在文档中都需描述,功能实现细节可在需求评审补充或
用例设计时加入)
需求评审(召开版本迭代会讨论明确需求)
版本迭代会:项目经理规划版本之时,召开版本迭代会,对需求进行说明,开发和
测试人员有问题可共同探讨,避免需求理解歧义。(其他组的经验)
问题2:完善需求OR完善用例?
讨论:
严格意义测试应该从需求开始抓起,参与需求的评审,对详细设计也要测试,如果可以做到这样,那么无需求测试的状况就不会出现了;但目前我们并没有做这么多,或者说做得不完全。所以测试人员都会或多或少的遇到这种无完善需求文档的测试状况,这时候我们需要谨记的则是我们必须保证我们的
测试用例包含了你所要测试对象的所有功能点。Openqs目前来看完善需求文档和完善用例都是比较痛苦的。因此建议是两头同时开展,一方面,系统的主要功能流程在需求文档(用需)补充。另一方面提高测试用例覆盖需求度,补充异常的验证流程等。
问题3:jira上的缺陷描述不规范。
解决:
全员规范缺陷描述,注意事项:
1. 对于无法重现或不好重现的问题,备注说明测试地址、账号、密码等,供开发人员排查。
2. 针对兼容性类的缺陷,说明缺陷使用的浏览器、分辨率、
操作系统等情况。
3. 1个缺陷尽量只描述1个问题,不要描述多个问题。避免开发人员对缺陷没有修改全,或者只修改一部分,一部分后面才修改。
4. 一些缺陷若文字描述无法准确表达的话,最好都能带上附件截图说明。比如一些提示信息啊、样式显示啊、另外一些显示代码类的错误都必须截图说明。
5. 开发人员解决完缺陷,若可能的话最好备注说明修改的情况及可能影响的功能。
6. 测试人员验证缺陷,若缺陷重新打开,增加备注说明验证的情况。
问题4:系统环境搭建较为复杂,测试环境更新未走标准化流程,系统安装更新手册说明不足。
解决:
自动构建、减少人工的配置操作、简化环境搭建和更新步骤
完善系统安装手册、系统维护手册、系统更新手册。
问题5:测试过程中,采用边测边改的方式,更新测试环境频繁,导致功能重复测试较多,测试效率不高,同时遗留缺陷的风险较大。
解决:
引入周更新测试,规范测试。
问题6:提交的缺陷,没有安排解决时间表,不断遗留和累积,感觉测试的工作成果得不到重视。 解决:
建议制定迭代计划时,将缺陷安排到迭代任务中解决,在迭代的
系统测试进行验证。
问题7:缺陷解决流程没有形成规范,存在缺陷关注不足,缺陷不解决,解决后未验证、无法验证等情况。
解决:
制定缺陷解决流程,按规范严格执行。
配置人员权限
功能测试阶段:
项目测试类型我觉得可先大致可分为两类:周更新测试和系统测试(含性能测试、安全测试、稳定性测试等专项测试)。
周更新测试
周更新测试:主要是针对每周的各组件更新需求,进行测试。
组件更新是否提交测试可由组件负责人自行判断,不一定需要提交测试。
提交测试需保证更新的主要功能已完成并已自测,若发现因为主要功能有问题导致无法继续测试,则退回测试。
提交测试需由组件负责人说明清楚更新的内容和可能的影响功能。
测试人员主要针对更新的内容及可能影响的功能进行测试,并对系统的主要流程进行冒烟测试,其他功能不进行测试。(1.增加的新功能以及新修复的bug。2.系统中重要的功能,如果有将测试用例分优先级的话,优先级高的测试用例应该要被执行到。3.与开发人员交流,确定哪些功能是受最新的改变而有可能发生问题的,开发人员认为最有可能出问题的功能,应该重点测试。)
测试结束一般不出具测试报告,可大概整理下早会说明或者通过邮件、QQ向组件负责人、项目经理、产品经理说明下测试情况。
一般在测试完毕后才允许更新环境进行第二轮的验证测试。(尽量减少测试过程中更新测试环境的频率,除非因出现大问题影响测试不得不更新环境)
周更新测试发现的缺陷统一登记到jira上。
系统测试
系统测试:建议比较大版本的功能开发结束,提交一次比较完整的系统测试。(包含之前做的几次周更新测试)
系统测试可按循环进行,第一循环测试后,在开发人员修改完第一循环缺陷后再提交第二循环测试。如此进过2-3个循环,最终使产品达到发布上线的标准。
系统测试结束后出具系统测试报告。
系统测试环境由测试人员搭建和更新。
提交测试时说明测试的内容(哪些要测试、哪些不测试)
系统测试准入条件
1. 需求文档已纳入SVN,功能需求明确,已通过评审。
2. 项目经理提交测试申请单(参考测试申请单模板)
3. 本迭代的测试用例已完成并通过评审。
由于版本周期问题,组内其他成员可能没时间看,测试人员可按优先级罗列测试点,测试计划。组内测试人员之间,也可以互评。若有时间产品经理和主要开发人员也可参与评审。
4. 版本开发结束,开发人员自测。
开发不宜提交太过频繁,不应提交无法构建,编译错误的代码。且应该在有较大改动,或进行较大更新时,有一定的可测性,才提交测试。若开发提交测试的产品,主流程主功能出现问题,较大影响测试开展,则测试人员可中止测试,待改善后重新进入。
相对独立的功能需求,可分别提交测试;但如果多个需求关联性紧密,应该开发完毕后一起提交测试,避免测试人员过多重复劳动。
对于没有完成的功能,不能提交测试,必须在代码中注释掉。
5. 代码已提交配置库,并提供安装说明文档。
注1:功能测试可以分测试循环进行,如第一循环测试完毕后,开发人员对缺陷进行修改,将大部分缺陷均已修改,然后提交第二循环测试。第二循环验证缺陷和进行针对性的测试。若缺陷还较多,再提交第三循环。如此反复直至满足测试退出准则结束功能测试。
注2:功能测试由于人力资源限制,提交太过频繁,建议有比较大改动或要进行较大更新才提交测试。其他类型采用周更新测试的方式,大体如现在我们进行的测试类型。
注3:若提交的产品,主要流程出现大问题,较大影响测试进行,向项目经理中止测试,待改善后再提交测试。
系统测试退出标准
1. 测试用例均已执行,用例通过率90%。
2. 功能需求都已经开发和测试完成,系统性能和功能已达到需求标准。
3. 严重缺陷均已修改(若有遗留由项目经理和产品经理审批)、总体缺陷修复率达到80%(暂定80%,后续根据产品要求可提高)
最近在写一个
移动工具类应用。需要支持离线功能,所以本地需要一份
数据库的拷贝,这样就涉及到移动端和服务器端数据库的同步问题。
在设计时我要满足以下几个需求:
1. 同步时双向传输数据最小化。双向即,服务器端更新同步到移动端,和移动端更新同步到服务器。每次只传输两端差异数据。
2. 支持离线。支持离线本身是一种好的用户体验,而它带来的一个其他的好处是每次移动端数据库查询仅需查询本地数据库,这样就避免了过多的服务器端查询。本地数据库减少了很多服务器的压力,当然也给用户省了流量。数据库更新操作也是如此,仅更新本地数据库,然后在适当的时机与服务器端进行同步。更进一步的说,移动端查询和更新数据只跟本地数据库打交道。
3. 冲突解决。如果一个用户帐号在多个移动端进行离线使用,势必会产生数据冲突。
设计的关键在于数据模型的设计,和同步算法。以下是我的想法。
下面是对象类代码,对应数据库的表字段。
服务器端设计:
public abstract class ServerBaseModel { public long userId; /* Global unique user id */ public long id; /* Model id. Unique for user */ public long lastmodified; /* Last modified server time stamp */ public boolean deleted; /* delete flag */ } |
移动端设计:
public abstract class ClientBaseModel { public long userId; <span style="font-family: Arial, Helvetica, sans-serif;">/* Global unique user id */</span> public long id; /* Model id. Unique for user */ public long lastmodified; /* Last modified server time stamp */ public boolean deleted; /* delete flag */ public boolean dirty; /* Local dirty flag */ } |
分析:
首先是如何选择表的主键id
1. 使用auto increment主键?不行!根据前面支持离线的需求,id应该在移动端就已经生成。如果使用auto increment在同一个用户帐号的情况下只可以做到单个移动端的唯一性,无法保证多个移动端的唯一性,更加不能保证服务器端全局的唯一性。
2. 使用UUID作为主键?可行!每一条数据在移动端创建时即为之生成UUID。这样基本可以保证服务器端全局的唯一性。对于使用UUID作为主键好不好的讨论很多,大家可以另行参考。
3. 我的方案。使用userId和一个用户唯一的model id作为联合主键。model id需要保证在同一userId下唯一,这样再加上userId使得数据全局唯一。问题是如何选择model id?一个比较可行但是不能保证完全没有重复的是时间戳。
4. 还有其他更好的主键方案吗?
接下来是如何判断服务器端数据已经更新
每一条数据存储一个last modified时间戳。这个时间戳是服务器端的时间。同一条数据如果移动端的lastmodified小于服务器端的lastmodified就可以判断数据已经更新。
移动端数据更新
移动端数据库增加一个dirty标志,dirty标志表示本地新增或者修改的数据,这些数据会在下一次同步时上传至服务器。
如何处理数据删除
根据前面last modified和dirty字段的设计,整个数据模型是一个增量式的。数据只允许新增和更新,所以这里增加一个deleted标志表示数据是否已经被删除。
以上介绍完我的移动端和服务器端数据库同步的数据模型设计,接下来讲讲详细同步算法。
不过。。。等等。。。公司年会的节奏,等有时间继续写。
同步算法:
1. 服务器端向移动端同步
2. 移动端向服务器端同步
android帐号验证框架
iperf 是一个 TCP/IP 和 UDP/IP 的性能测量工具,能够提供网络吞吐率信息,以及震动、丢包率、最大段和最大传输单元大小等统计信息;从而能够帮助我们
测试网络性能,定位网络瓶颈。iperf是开源的,源代码可以从http://sourceforge.net/projects/iperf/下载。
1. iperf能够做什么
提起iperf,想必大家都知道它是用了测试网络性能的。具体说来,Iperf是美国伊利诺斯大学(University of Illinois)开发的一种开源的网络
性能测试工具。可以用来测试网络节点间(也包括回环)TCP或UDP连接的性能,包括带宽、抖动以及丢包率,其中抖动和丢包率适应于UDP测试,而带宽测试适应于TCP和UDP。
这里需要特别提出的是,iperf不能够用来测试时延,想一想这是为什么。
2. 网络性能参数
以上提到了网络的主要性能参数包括带宽,时延,抖动和丢包率,这些用一个名词代替,就是QOS(服务质量)。
对于时延和抖动,见如下图
图中D1,D2分别表示包A和包B的时延。
抖动=|D2-D1|
对于时延,iperf无能为力。但是iperf能够计算抖动,想想这又是为什么。
我们知道,在iperf中,我们测试时需要发送大量的包,因此计算出来的抖动值就是连续发送时延差值的平均值。
3. 安装iperf
在Unix系统下,安装iperf最方便的方法是直接下载rpm包,使用rpm指定安装即可。
当然也可以直接去sourceforge上下载源代码,使用如下命令安装即可。
#./configure
#make
#make install
前提是该机器上已经有C++编译器和make等程序。安装完成之后,可以进行一个简单的回环测试iperf是否安装成功。
$ iperf -s ------------------------------------------------------------ Server listening on TCP port 5001 TCP window size: 85.3 KByte (default) ------------------------------------------------------------ [ 4] local 127.0.0.1 port 5001 connected with 127.0.0.1 port 35589 [ ID] Interval Transfer Bandwidth [ 4] 0.0-10.0 sec 26.3 GBytes 22.6 Gbits/sec $ iperf -c 127.0.0.1 ------------------------------------------------------------ Client connecting to 127.0.0.1, TCP port 5001 TCP window size: 49.5 KByte (default) ------------------------------------------------------------ [ 3] local 127.0.0.1 port 35589 connected with 127.0.0.1 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-10.0 sec 26.3 GBytes 22.6 Gbits/sec |
iperf测试案例介绍
4. iperf主要参数
iperf中的可选参数比较多,具体可以参见其用户手册。
http://webfolder.wirelessleiden.nl/iperf/
一般来说,我们在做性能测试的时候需要指定包长,不同的包长会得到不同的吞吐量,通过-l指定,而使用-b指定带宽。
5. 测试吞吐量,抖动和丢包率
如何需要同时测试以上三个参数,那么只能通过UDP获得。使用-u参数进行UDP测试(iperf默认为TCP)。
在测试的最后server端会给出一个报告。
[ 3] local 192.168.1.1 port 2152 connected with 192.168.101.2 port 56768 [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams [ 3] 0.0- 1.0 sec 1.40 MBytes 11.7 Mbits/sec 0.069 ms 0/14671 (0%) [ 3] 1.0- 2.0 sec 1.40 MBytes 11.8 Mbits/sec 0.050 ms 0/14703 (0%) [ 3] 2.0- 3.0 sec 1.40 MBytes 11.8 Mbits/sec 0.052 ms 0/14708 (0%) [ 3] 3.0- 4.0 sec 1.40 MBytes 11.8 Mbits/sec 0.057 ms 0/14704 (0%) [ 3] 4.0- 5.0 sec 1.40 MBytes 11.8 Mbits/sec 0.072 ms 0/14706 (0%) [ 3] 5.0- 6.0 sec 1.40 MBytes 11.8 Mbits/sec 0.075 ms 0/14705 (0%) [ 3] 6.0- 7.0 sec 1.40 MBytes 11.8 Mbits/sec 0.060 ms 0/14707 (0%) [ 3] 7.0- 8.0 sec 1.40 MBytes 11.8 Mbits/sec 0.073 ms 0/14703 (0%) [ 3] 8.0- 9.0 sec 1.40 MBytes 11.8 Mbits/sec 0.073 ms 0/14706 (0%) [ 3] 0.0-10.0 sec 14.0 MBytes 11.8 Mbits/sec 0.064 ms 0/147020 (0%) |
要获得带宽数据,需要不断在client端增加带宽值,直到server端出现轻微的丢包为止,此时server端显示的带宽就是被测系统的吞吐量。
6. 测试时延
那么有朋友会问,iperf不能用来测试时延,而时延又是比较重要的QOS参数,有什么办法吗?
其实最简单的办法就是使用Ping程序。我们经常用它来测试特定主机能否通过IP到达,
程序会按时间和反应成功的次数,估计丢包率和分组来回时间(即网络时延)。
当然,如果我们能成功构造一个回环测试路径,那么测试时延就轻而易举了,我们可以使用iperf发送数据,同时结合tcpdump抓包工具,经过wireshark分析.cap文件就可以得出包来回时间,也就是往返时延。
7. 使用TCP测试带宽应注意的问题
有时候,我们需要使用TCP来测试网络带宽。这里有一个参数需要特别注意,那就是TCP窗口大小,可以使用-w参数指定。
网络通道的容量capacity = bandwidth * round-trip time
而理论TCP窗口的大小就是网络通道的容量。
比如,网络带宽为40Mbit/s,回环路径消耗时间是2ms,那么TCP的窗口大小不小于40Mbit/s×2ms = 80kbit = 10Kbytes
此时我们可以查询iperf默认的TCP窗口大小来决定是否需要设置此参数,在此例中,窗口大小应设计大于10Kbytes,当然,这仅仅是理论值,在实际测试中可能需要作出调整。
参数说明
-s 以server模式启动,eg:iperf -s
-c 以client模式启动,host是server端地址,eg:iperf -c 222.35.11.23
通用参数
-f [k|m|K|M] 分别表示以Kbits, Mbits, KBytes, MBytes显示报告,默认以Mbits为单位,eg:iperf -c 222.35.11.23 -f K
-i sec 以秒为单位显示报告间隔,eg:iperf -c 222.35.11.23 -i 2
iperf是client端向server端发送数据
server端显示的是接收速率,最好加i参数,进行速率跟踪
client 显示的是发送速率
server 显示接收速率
-l 缓冲区大小,默认是8KB,eg:iperf -c 222.35.11.23 -l 16
可以使用不同的包长,进行测试
-m 显示tcp最大mtu值
-o 将报告和错误信息输出到文件eg:iperf -c 222.35.11.23 -o c:iperflog.txt
-p 指定服务器端使用的端口或客户端所连接的端口eg:iperf -s -p 9999;iperf -c 222.35.11.23 -p 9999
-u 使用udp协议
测试htb的时候最好用udp,udp通信开销小,测试的带宽更准确
-w 指定TCP窗口大小,默认是8KB
如果窗口太小,有可能丢包
-B 绑定一个主机地址或接口(当主机有多个地址或接口时使用该参数)
-C 兼容旧版本(当server端和client端版本不一样时使用)
-M 设定TCP数据包的最大mtu值
-N 设定TCP不延时
-V 传输ipv6数据包
server专用参数
-D 以服务方式运行ipserf,eg:iperf -s -D
-R 停止iperf服务,针对-D,eg:iperf -s -R
client端专用参数
-d 同时进行双向传输测试
-n 指定传输的字节数,eg:iperf -c 222.35.11.23 -n 100000
-r 单独进行双向传输测试
-b 指定发送带宽,默认是1Mbit/s
在测试qos的时候,这是最有用的参数。
-t 测试时间,默认10秒,eg:iperf -c 222.35.11.23 -t 5
默认是10s
-F 指定需要传输的文件
-T 指定ttl值
测试实例:
使用:
此软件需要安装到两端需要互测的机器上,然后一段作为服务端监听,一端作为客户端连接。具体命令可以iperf -h 查看下。
服务端:
iperf -s -u
-s 标记此端为服务端
-u标记自己为UDP监听
-p 指定自己监听端口
客户端:
iperf -c 1.1.1.1 -i 1 -u -t 60 -F /root/a.zip -P 5
-c标记自己为客户端
-i 设定输出值间隔
-u使用传输协议为UDP
-t 设定测试时间为60秒
-F 指定传输文件(该项可有可无)
-P 指定进程数,如果设置为5,那么也就相当与对端建立五个连接
注意事项:
1.发包测试需要分为UDP测试与TCP测试,其中服务端需要用-u命令去区分监听协议。
2.TCP协议测试不能计算出时延与丢包率,而且还不能指定发送带宽。
案例:
[root@localhost ~]# iperf -c 192.168.1.100 -u -i 1 -t 10 -b 2M #指定2M带宽向对端发送数据 ------------------------------------------------------------ Client connecting to 192.168.1.100, UDP port 5001 Sending 1470 byte datagrams UDP buffer size: 108 KByte (default) ------------------------------------------------------------ [ 3] local 192.200.40.112 port 32784 connected with 218.60.1.20 port 5001 #与对端连接上的信息 [ ID] Interval Transfer Bandwidth #输出行的解释 [ 3] 0.0- 1.0 sec 12.0 GBytes 103 Gbits/sec [ 3] 1.0- 2.0 sec 244 KBytes 2.00 Mbits/sec [ 3] 2.0- 3.0 sec 244 KBytes 2.00 Mbits/sec [ 3] 3.0- 4.0 sec 244 KBytes 2.00 Mbits/sec [ 3] 4.0- 5.0 sec 244 KBytes 2.00 Mbits/sec [ 3] 5.0- 6.0 sec 245 KBytes 2.01 Mbits/sec [ 3] 6.0- 7.0 sec 244 KBytes 2.00 Mbits/sec [ 3] 7.0- 8.0 sec 244 KBytes 2.00 Mbits/sec [ 3] 8.0- 9.0 sec 244 KBytes 2.00 Mbits/sec [ 3] 9.0-10.0 sec 244 KBytes 2.00 Mbits/sec [ 3] 0.0-10.0 sec 12.0 GBytes 10.3 Gbits/sec [ 3] Sent 1702 datagrams [ 3] Server Report: #输出报告 [ 3] 0.0-10.6 sec 263 KBytes 204 Kbits/sec 36.313 ms 1518/ 1701 (89%) #间距 ,传输总字节,速率,时延,丢包率。 [ 3] 0.0-10.6 sec 1 datagrams received out-of-order #乱序 |
服务端:
[root@localhost ~]# iperf -s -u #设定此端为服务端 ------------------------------------------------------------ #本段设置信息 Server listening on UDP port 5001 Receiving 1470 byte datagrams UDP buffer size: 108 KByte (default) ------------------------------------------------------------ [ 3] local 218.60.1.20 port 5001 connected with 218.241.145.36 port 32784 #如果对端连接上了本端才会出现该信息 [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams [ 3] 0.0-10.6 sec 263 KBytes 204 Kbits/sec 36.314 ms 1518/ 1701 (89%) [ 3] 0.0-10.6 sec 1 datagrams received out-of-order |
iPerf同样也可以用于测量UDP数据包吞吐量、丢包和延迟指标。与TCP测试不同的是,UDP测试不采取尽可能快地发送流量的方式。与之相对的是,iPerf尝试发送1 Mbps的流量,这个流量是打包在1470字节的UDP数据包中(成为以太网的一帧)。我们可以通过指定一个目标带宽参数来增加数据量,单位可以是Kbps 或Mbps(-b #K 或 --b #M)。举例如下:
测量UDP丢包和延迟
然而,上面的例子只说明了iPerf客户端能够以多快的速度传输数据。为了得到更多关于UDP发送的数据,我们必须查看服务器上的结果:
测量UDP丢包和延迟
这样,我们就可以看到吞吐量(间隔1秒测量的),以及丢包数(丢失的数据屯接收到的数据对比)和延迟(如jitter——在连续传输中的平滑平均值差)。延迟和丢失可以通过应用的改变而被兼容。比如,视频流媒体通过缓冲输入而能够容忍更多的延迟,而语音通讯则随着延迟增长性能下降明显。
UDP测试可以通过改变报文缓冲长度进行优化,长度单位为Kbytes 或 Mbytes(-l #K or #M)。与以太网帧的1500比特的MTU(最大转换单位)不同的是,802.11数据帧可以达到2304比特(在加密之前)。
但是,如果你正在测试的路径中包括Ethernet和802.11,那么要控制你的测试数据包长度,使它在一个Ethernet帧以内,以避免分片。
另一个有趣的iPerf UDP测试选项是服务类型(Type of Service, ToS),它的大小范围从0x10 (最小延迟) 到0x2 (最少费用)。在使用802.11e来控制服务质量的WLAN中,ToS是映射在Wi-Fi多媒体(WMM)存取范畴的。
对比两种方式
在802.11a/b/g网络中,无线电的传输性能变化在在两个方向上都很相似。比如,当距离导致数据传输率下降或干扰造成重要数据包丢失时,发送和接收的应用吞吐量都受到影响。
在802.11n网络中,MIMO天线和多维空间流使问题又有所不同。从笔记本发送到AP上的数据帧可能(有意地)使用一个完全与从AP发送到笔记本上帧时不同的空间路径。这样的结果是,现在对两个方向的测试都很重要的。幸运的是,iPerf本身就已经拥有这个功能,这是由两个选项所控制的:
--d选项是用于告诉iPerf服务器马上连接回iPerf客户端的由--L 所指定端口,以支持同时测试两个方向的传输。
--r选项虽然有些类似,但是它是告诉iPerf服务器等到客户端测试完成后再在相反的方向中重复之前的测试。
最后,如果你需要支持多点传送应用,那么可以使用-B选项指定多点传送组IP地址来启动多个iPerf服务器。然后再打开你的iPerf客户端,连接之前启动的多点传送组iPerf服务器。
使用测试工具iPerf监控无线网络性能:图形化测试结果
如本文介绍的,iPerf程序可以在命令行下运行,它或者也在一个名为JPerf的Java实现前端工具上运行。JPerf不仅能简化复杂命令行参数的构造,而且它还保存测试结果——同时实时图形化显示结果。
图1 使用JPerf运行iPerf
事实上,iPerf测试工具被嵌入到一些其它的网络流量分析工具中——包括底层LAN分析工具,如AirMagnet。比如,在下面的屏幕截图中显示了一个Wi-Fi笔记本上运行的AirMagnet作为iPerf客户端与安装在有线网络上的常规iPerf服务器进行交互的情况。
图2 使用AirMagnet运行iPerf
总结
正如我们所看到的,iPerf简化了对基于TCP数据流应用和UDP数据包应用的端对端性能测定。然而,iPerf仍然无法模拟所有类型的应用——比如,对于交互式上网的模拟就不是很好。同时,用于iPerf Wi-Fi测试的WLAN适配器也会影响你的测试——为了获得更好的测试结果,我们可以配置一个类似于“实际”用户的有代表性的适配器。
尽管如此,iPerf仍然是一个非常方便的工具,它可以帮助你生成和检测WLAN应用流量。同时,因为iPerf可以方便以开源软件方式获取,因此使用它来在其它位置重复创建测试环境是一个很好的方法——如分公司、供应商技术支持等等。想要了解更多关于iPerf的信息,可以阅读NLANR上的更老的文件或者浏览SourceForge上新的项目页面。
环境:CentOS 6.4, hadoop-2.0.0-cdh4.2.0, JDK 1.6, spark-0.8.0-incubating-bin-cdh4.tar.gz,Scala 2.9.3
1. 安装、部署集群环境
参考前章《安装Spark 0.8集群(CentOS6.4) - 大数据之内存计算》
使用在线测试数据生工具,动态生成如下json数据(名称DATA[1-9].json):
{"id":10,"first_name":"Ralph","last_name":"Kennedy","country":"Colombia","ip_address":"12.211.41.162","email":"rkennedy@oyonder.net"},
{"id":11,"first_name":"Gary","last_name":"Cole","country":"Nepal","ip_address":"242.67.150.18","email":"gcole@browsebug.info"},
…
可以数据可以先生成100M左右,然后通过linux cp / cat工具进行数据复制、合并,产生不同大小数据,方便测试。
测试任务:
对所有*.json数据的ip地址进行简单统计,包括:ip地址总数统计,“241.*”ip地址段总数统计。将其上传到HDFS集群上
2.1启动 Spark 集群
在master上执行
$>cd ~/spark-0.8.0 $>bin/start-all.sh |
检测进程是否启动
$> jps 11055 Jps 2313 SecondaryNameNode 2409 JobTracker 2152 NameNode 4822 Master |
浏览master的
web UI(默认http://localhost:8080). 这是你应该可以看到所有的word节点,以及他们的CPU个数和内存等信息。
2.2运行spark-shell从HDFS读取文件并统计IP地址
// set the master node of spark cluster and runspark-shell $> MASTER=spark://centos01:7077./spark-shell // read the json data $>val file = sc.textFile("hdfs://sdc/user/hadoop/In/DATA*.json") // filter the json data $>val ips = file.filter(line => line.contains("ip_address")) // Count all the IP $>ips.count() // Count all the“241.*”IP $>ips.filter(line => line.contains("241.")).count() $>ips.filter(line => line.contains("241.")).collect() |
2.3 运行结果