测试设计入门
我们在这一章中提供的信息,对测试自动化的新手和有经验的QA专业人士都是有帮助的。此处我们描述最公共的自动化测试类型。我们也描述常用的、在测试自动化中的“设计模式”,用于改善你的自动化测试集的可维护性和可扩展性。富有经验的读者将觉得这些内容是有意思的,如果还没有使用这些技术。
测试的类型
你应测试你的应用程序的那个部分?这依赖于你的项目的各个方面:用户的期望,项目允许的时间,项目经理设置的优先级等等。一旦项目的边界被定义,你,作为测试者,将必定可以做出测试内容的决定。
在此,我们创建了几个术语,用于分类你可能在你的Web应用程序上执行的测试的类型。这些术语绝不是标准,尽管我们在此提出的概念典型地用于Web应用程序的测试。
静态内容测试
最简单的测试类型,内容测试,是一个简单的对静态的、没有变化的、UI元素的存在性测试。例如:
- 每一页是否有预期的页面标题?这可以被用于验证你的,在追随一个连接后发现一个预期的页面的测试。
- 应用程序的主页包含一个预期的图像在页面的顶端么?
- Web站点的每一页包含一个带有到公司联系人页面的链接,隐私权策略,和商标信息的页脚区域么?
- 每个页面开始于带有<h1>标记的标题文本么?并且,每一页有正确的文本在那个标题中么?
你可能或可能不需要内容测试。如果你页面的内容不太可能被影响,那么手工地测试页面内容可能是更有效率的。如果,例如,你的应用程序涉及的文件被移动到不同的位置,内容测试可能是有价值的。
链接测试
Web站点的频繁的错误来源是中断的链接,或缺失链接后面的页面。测试涉及点击每个链接并验证预期的页面。如果静态链接不是频繁地改变则手工测试可能是有效的。然而,如果你的Web设计者频繁地变更链接,或文件偶尔被重新定位,链接测试应该被自动化。
功能测试
这些是在你的应用程序中的特定功能的测试,需要某些类型的用户输入,并且返回某些类型的结果。时常一个功能测试会涉及多个页面,每个带有基于窗体的输入页面,包含输入域的集合,提交和取消操作,以及一个或多个响应页面。用户输入可以通过文本输入域,复选框,下拉列表框或任何浏览器支持的输入。
功能测试时常是你将自动化的最复杂的测试,但通常也是最重要的。典型的测试可能是登录,注册到站点,用户帐号操作,帐号设置改变,复杂的数据存取操作,以及其他。功能测试典型地反映用户的使用场景,用于限定功能,设计或你的应用程序。
动态元素测试
时常一个Web页面元素有一个唯一的标识符,用于唯一地在页面中定位那个元素。通常这被实现使用html标记的“id”或“name”属性。这些名称可能是静态的,诸如不变的字符串常量。它们也可以是动态生成的值,将随每个页面的示例而变化。例如,某些Web服务器可能在一个页面实例中命名一个显示的文档为doc3861,在另一个不同的页面实例中为doc6248,取决于用户存取啥文档。一个验证那个文档存在性的测试脚本,可能没有一个一致的标识符用于定位那个文档。时常,带有可变的标识符的动态元素是,在基于用户动作的某个类型的结果页面上。这必定依赖于Web应用程序的功能。
这是一个示例:
<input type="checkbox" value="true" id="addForm:_ID74:_ID75:0:_ID79:0:
checkBox"/>
这显示一个HTML复选框。它的id(addForm:_ID74:_ID75:0:_ID79:0:checkBox)是一个动态生成的值。下次打开相同的页面,它可能是一个不同的值。
Ajax测试
Ajax是支持动态改变用户接口元素的,可以动态改变元素而无需浏览器重新装载页面的技术,诸如动画,RSS订阅,以及实时的数据更新等等。有无数的方法,Ajax可以被用来更新页面上的元素。但,在Ajax驱动的应用程序中,被认为是最容易的方式是,数据可以从应用程序服务器存取,然后显示在页面上,而无需重新装载整个页面。只有页面的一部分,或严格地说元素自身被重新装载。
验证结果
断言和验证
何时应该使用断言(assert)命令,何时应该使用验证(verify)命令?这取决于你。差别在于当检查失败的时候你想要什么发生。你想要你的测试终止,或继续并简单地记录失败的检查么?
这是需要权衡的。如果你使用一个断言,测试将停止在那个点,不运行任何随后的检查。有时,或许是经常,那就是你想要的。如果测试失败,你会立即知道测试没有通过。测试引擎,诸如TestNG和JUnit有常用的开发环境(第五章)的插件,可以方便地标记这些测试做失败的测试。优点:你可以立即看到检查是否通过。缺定:当一个检查失败时,其他的检查将永远也不会执行,这样你就无法得到有关它们状态的信息。
反之,验证命令不会终止测试。如果你的测试仅仅使用验证命令,不论检查是否找到缺陷你可以确保(假如没有非预期的异常出现)测试会运行完成。缺点:你必须做更多的检查测试结果的工作。也就是说,你不会得到回馈从TestNG或JUnit。你将需要检查控制台打印输出或日志输出的结果。并且你每次运行测试,你都需要花费时间浏览整个输出。如果你正在运行数百个测试,每一个有自己的日志,这将是一个耗时的工作,则即时反馈的断言可能是更合适的。由于即时的反馈,断言比验证更常用。
权衡: assertTextPresent, assertElementPresent, assertText
你现在应该熟悉这些命令,和使用它们的技术。如果不熟悉,请首先参考第三章。当构造你的测试时,你将需要决定:
- 我仅仅检查页面上的文本存在么?(verify/assertTextPresent)
- 我仅仅检查页面上的HTML元素存在么?也就是说,文本,图像,或其他内容不做检查,仅仅和HTML标记相关。 (verify/assertElementPresent)
- 我必须测试两者,元素和它的内容么?(verify/assertText)
没有直接的答案。它依赖于你的测试的需求。当然,取决于你测试的应用程序的需求。如果不能肯定,使用assertText,因为这是最严格的检查类型。你可以稍后改变它,但至少你不会错过任何潜在的失败。
Verify/assertText是最特定的测试类型。它会失败,如果HTML元素(标记)或文本不是你的测试所预期的。或许你的Web设计者正在频繁地改变页面,你不希望你的测试在每次他们做出改变时测试失败,因为这些改变是预期的。然而,假定你仍然需要检查页面上的某些东西,比如一个段落,或标题文本,或一个图像。在这种情况下,你可以使用verify/assertElementPresent。这将确保特定类型的元素存在(如果使用XPath还可以确保元素相对于页面上的其它元素存在)。但你不关心内容是啥。你仅仅关注一个特定的元素,比如说,一个图像在特定的位置。
要对做出这些类型的决定有感觉需要一些时间和一点点经验。它们是容易的概念,容易在你的测试中做出改变。
定位策略
选择一个定位策略
在页面中选择对象有许多种方法。但啥是这些定位类型的选择依据?回顾一下我们可以定位一个对象使用:
- 元素的id
- 元素的name属性
- 一个XPath语句
- 一个链接文本
- 对象文档模型(DOM)
从测试的性能方面来说,假定页面源代码中的id或name属性具有良好的命名,使用id或name属性定位器是最有效的,而且是使你的测试代码更可读的。XPath语句需要花费更长的时间进行处理,因为浏览器必须运行它的XPath处理器。在Internet Explorer 7中,XPath据知是特别慢。通过链接文本定位时常是便利和性能良好的。尽管这个技术是特定于链接。同样,如果链接文本可能频繁改变,使用<a>元素定位可能是更好的选择。
尽管有时,你必须使用XPath定位器。如果页面源代码没有id或name属性,你可能不得不选择使用XPath定位器。(DOM定位器不再常用,因为XPath可以做它可以做的任何事情甚至更多。DOM定位器是可得到的,仅仅为了支持遗留的测试。)
使用XPath有一个使用id或name属性定位没有的优点。使用XPath(和DOM)你可以定位一个对象与页面上另外一个对象的关系。例如,如果一个链接必须出现在一个<div>节的第二个段落里,你可以使用XPath来指定。使用id和name定位器,你只可以指定它们在页面上出现,也就是说,在页面的某个地方。如果你必须测试一个图像,显示在公司的logo出现在页面的顶端,在一个页头,XPath可能是更好的选择。
定位动态元素
正如在较早的,有关测试类型一节所描述的,一个动态元素是一个页面元素,它的标识符随每个页面的实例而变化。例如,
<a class="button" id="adminHomeForm" οnclick="return oamSubmitForm('adminHomeForm',
'adminHomeForm:_ID38');" href="#">View Archived Allocation Events</a>
这个HTML锚标记定义一个带有“adminHomeForm”id属性的按钮。与大多数HTML标记比较,这是一个相当复杂的锚标记,但它仍然是一个静态标记。这个HTML在每次浏览器装载这个页面的时候都是相同的。它的id保持不变,在所有这个页面的实例中。也就是说,当这页被显示的时候,这个UI元素总是有这个标识符。如此,对你的点击这个按钮的测试脚本,仅仅需要使用下面的selenium命令。
click adminHomeForm
或者,在Selenium 1.0中
selenium.click("adminHomeForm");
你的应用程序,不管怎样,可能动态生成HTML,在那里对Web页的不同实例标识符可能是变化的。例如,对一个动态页面,HTML可能看起来像这样。
<input type="checkbox" value="true" id="addForm:_ID74:_ID75:0:_ID79:0:checkBox"
name="addForm:_ID74:_ID75:0:_ID79:0:checkBox"/>
这定义一个复选框。它的id和name属性(addForm:_ID74:_ID75:0:_ID79:0:checkBox)是动态生成的值。在这种情况下,使用一个标准的定位器可能看起来像下面的。
click addForm:_ID74:_ID75:0:_ID79:0:checkBox
或者,在Selenium RC
selenium.click("addForm:_ID74:_ID75:0:_ID79:0:checkBox");
对给定的动态生成的标识符,这种方法可能不工作。下次页面装载,标识符可能是一个不同于使用在Selenium命令中的值,并因此将不会被找到。点击操作将失败带有一个“element not found(元素没有找到)”错误。
要纠正这一点,一个简单的解决方案是仅仅使用XPath定位器,而不是试图去使用id定位器。如此,对这个复选框你可以简单地使用:
click //input
或者,如果它不是第一个在页面上的input元素(它很可能不是),可以试一下一个更详细的XPath语句:
click //input[3]
或
click //div/p[2]/input[3]
如果不管如何,你都需要使用id去定位这个元素,则需要一个不同的解决方案。你可以从Web站点捕捉这个id,先于使用它在一个Selenium命令。它可像这样做。
String[] checkboxids = selenium.getAllFields(); // Collect all input IDs on page.
for(String checkboxid:checkboxids) {
if(checkboxid.contains("addForm")) {
selenium.click(expectedText);
}
}
这个方法是可行的,如果只有一个复选框的id包含“expectedText”文本。
定位Ajax元素
正如提供在上面的测试类型节,一个带有Ajax实现的页面元素是一个可以被动态刷新的元素,而不必刷新整个页面。定位和验证一个Ajax元素的最佳方式是使用Selenium 2.0 WebDriver API。这是特别设计用来进行Ajax元素的测试的,在测试Ajax元素方面,Selenium 1.0 有一些限制。
在Selenium 2.0,你使用waitFor()方法,等待一个页面元素变成可得到的。这参数是一个WebDriver实现的定位器By对象。这个被详细地解释在WebDriver一章。
要使用Selenium 1.0(Selenium RC)完成这个,涉及更多的代码,但也不困难。方法是去检查这个元素,如果它是不可得到的,则等待一个预定的时间段,然后再一次检查它。用一个带有预定义的超时,如果元素仍然没有发现就终止这个循环的,循环执行这个。
让我们考虑一个带有一个链接(link=ajaxLink)的页面,在点击一个页面上的按钮时(没有刷新页面)。这可以使用一个for循环,用Selenium来处理。
// 循环初始
for (int second = 0;; second++) {
// 如果循环到达60秒,则中断这个循环
if (second >= 60) break;
// 搜索元素"link=ajaxLink" ,如果可得到则中断循环
try { if (selenium.isElementPresent("link=ajaxLink")) break; } catch (Exception e) {}
// 暂停1秒
Thread.sleep(1000);
}
这肯定不是惟一的解决方案。Ajax在用户论坛是一个公共的主题,我们推荐搜索一下以前的讨论,看看其他人已经做了那些事情。
© Copyright 2008-2012, Selenium Project. Last updated on Feb 02, 2012.