上一篇文章我们用工时记录的xpath个数验证了搜索结果和实际结果是否匹配。但是,光检查工时表里的记录数目对不对还不够,考虑这么一种情况:万一数目匹配,但最后显示的记录里多了一个其它员工的记录怎么办?假设我搜索Tester One返回了如下结果:
多了一个Tester Two的记录。Tester One的xpath数目与在Excel表中的期望结果都是2,但实际搜索出来的总工时数是3。所以,我们还需要进一步对每一条数据进行验证。
搜索栏中输入Tester One先拿到每条记录:
我的思路是逐行读取每条记录,每条有9列数据,把它们读到list里然后再与输入数据进行比较。
既然是逐行读取,那肯定要用循环语句。我们已经在登录那个例子中读文件的操作里使用到了for语句。现在介绍一种更容易理解的写法。如果用java写For循环语句怎么写?是不是for(int i = 0; i < count; i++)?这里的写法差不多,看下图,在Search Timesheet By Employee Name中继续写第二个检查点:
第9行写个comment,告知开始进行第二个检查点的测试。第10行就是for循环的写法,只是这里我把循环变量$i的初始值设成1。注意,虽然第5列写的是${timesheetCount}+1,可实际最后一次循环的值是${timesheetCount}。如果这里你写${timesheetCount},那循环就进行到${timesheetCount}-1就停了,永远在前一个停,这里请注意。
很显然,由于${timesheetCount}等于2,我们的循环语句只会循环两次,每次拿出一条记录。上篇文章已经用firebug找到了工时记录的xpath了:
它们的xpath分别为:
第一条://table/tbody/tr[1]
第二条://table/tbody/tr[2]
用xpath可以访问数据:比如第一条数据的访问结果如下:
如果这里没看懂的朋友请再复习下xpath的相关知识。没问题后我们在ObjectRepository -> Timesheet.html中为xpath声明变量${locTSTable_Records_Fields}:
这里我在xpath后面加了/td,以便一会儿访问数据。又写了个可替换的||ROW NUMBER||,因为第一次循环||ROW NUMBER||是1,第二次是2,以此类推:
第13行就是替换过程,${i_string}是${i}的字符串形式。上篇文章我们有一步避免了使用转换,这次很不幸,躲不过去了。${i}在循环里默认是整型的,是integer,而Replace String不能用整型替换字符串,所以要在第12行转换一下。转换操作的关键字是Convert To String,参数就是一个integer,转换完可以赋给一个字符串变量。在API文档上我们还能看到ConvertTo Integer,Convert To Boolean等等,其实都是为了转化设计的。
第13行第二列局部变量${locTSTable_Records_Fields_Row}存的是${locTSTable_Records_Fields}替换后的字符串,它的值在第一次循环执行到这里时应该是//table/tbody/tr[1]/td,看到这儿你就知道为什么我当时把${i}的初值设置成1了吧?可以通过循环变量把tr的角标索引巧妙赋值,因为每次循环读一条记录,其实就是读一条xpath。好,现在我要把这条记录里的每列数据都读入到list里,然后一个一个和搜索关键字Tester One对比,看有没有一样的。如果有就证明工时记录里包含关键字,测试通过,反之则失败。有人说了,你傻啊,直接对比第一列姓名不就完了么?是这样,有时候搜索功能并不在乎数据出现的位置,只要出现就算搜索成功。再一个,我也通过这么一种较复杂的验证过程给大家讲讲如何声明和使用list。
右击Pages -> Timesheet.html创建Keyword“Get All Timesheet Fields”,用来写把数据读到list的过程。它有一个参数叫${locTSTable_Records_Fields_Row_Current},这个参数指的是当前的某一行,也就是某个tr。如果这是第一次循环,传进来的值就是//table/tbody/tr[1]/td。
如下图,第1行创建了一个空的list,之前说过,list的前缀是“@”,而创建list的关键字是Create List。如果想取每列数据还要用For循环语句进行遍历,每条工时记录是9列,我们完全可以用9+1=10作为循环终值。可这样不好,因为如果开发人员以后每次加了一列或是减了一列那我们还得重新修改函数,很不方便,也不灵活。最好让程序自动得知当前有多少列,不要把它写死。所以第2行我还是用Get Matching Xpath Count,得到这条记录/tr的xpath里有多少个td,也就是多少列,赋给一个叫${fieldCount}的局部变量:
循环过程如下3-6步:
再看一遍这7列数据作为提示:
字有点小,不过还是能看清楚的。这个例子中循环需要进行7次,但终值为8,所以在第三行用${fieldCount}+1表示。总共7个数据。看到这里你会发现什么?我们还可以用循环变量${j}当td的角标索引。当前参数是//table/tbody/tr[1]/td,我们需要做的就是第4行的连接字符串。这里我们和上次连接字符串的情况不同,td和[${j}]之间不需要空格,所以需要加个Separator=来区分。不明白的Ctrl+ALT+Space打开API就懂了。如果这是第一次循环,第4步结束的结果就应该是${locTSTable_Records_Fields_Column_Current} = //table/tbody/tr[1]/td[1]。第5行就是用Get Text关键字对xpath进行简单的取值,第一次循环得到“Tester One”。最后一步,得到的数据放到咱们之前声明的list中,用到的关键字是Append to List。这个关键字属于一个名叫Collections的library,用之前请先引入ApplicationSpecific.html:
Append To List接受两个参数,第一个是目标list,这里的用${fieldList}表示;是第二个是要放进list的数据变量。有人问为什么${fieldList}不写成@{fieldList},它不是个List吗?注意,List只有在声明和作为返回值的时候才用“@”表示,赋值过程把它当成一个变量来写。
7次循环过后,数据全部进入list。然后返回list到之前的函数,我直接在Return Value那里写了:
List返回之后就简单了,直接看看包不包含搜索关键字呗,用的语句是List Should Contain Value。在Search Timesheet By Employee Name中调用Get All Timesheet Fields:
List Should Contain Value也是个布尔函数,判断列表里有没有目标数据。它有两个参数,列表以及目标数据。有的话测试通过,没有测试失败。
写完这一步,整个程序的脉络就清晰多了。首先对记录进行循环,每读取一条记录,就在第13行调用Get All Timesheet Fields传入当前记录的xpath拿到所有列,然后通过对列的循环取值放入list,最后看list里有没有要搜索的文字。虽然复杂,但一切看起来都正确。
到现在只是做完搜索Tester One的,那另外三个员工呢?还是读文件那些东西,每次从文件里读一个员工姓名以及期望的工时数目,然后调用Search Timesheet By Employee Name执行剩下的搜索操作。再建一个Keyword叫Search Timesheet作为这一系列操作的总函数,代码如下:
最后回到TCTIMES-1,在第7行和第8行加上总函数Search Timesheet,这个test case就写完了:
运行TCTIMES-1,测试通过。这就是关于工时表的第一个Test case,之后我会再带着大家看一个例子。
这篇文章的源代码在[Test10](https://github.com/cslm/cslm.robotframework/tree/master/Test10)里。