第 07 章Junit 单元测试框架
对于不熟悉编程的测试新手来讲,单元测试是个听起来高大上的话题,貌似只有高级测试或开发人员才能胜任这项工作。其实,它并非想象的那么高级,本章我们就来揭开单元测试面纱。
可能还有个疑问,我们不是在学 Web 自动化么?为什么要去学习单元测试框架,又不是教我们写单元测试的文章。可没有人告诉你单元测试框架只能用于代码级别的测试。对于单元测试框架来讲,个人认为它主要完成以下三件事。
● 提供用例组织与执行:当你的测试用例只有几条时,可以不必考虑用例的组织,但是,当测试用例达到成百上千条时,大量的测试用例堆砌在一起,就产生了扩展性与维护性等问题,需要考虑用例的规范与组织问题了。单元测试框架就是用来解决这个问题的。
● 提供丰富的比较方法:不论是功能测试,还是单元测试,在用例执行完成之后都需要将实际结果与进行预期结果的进行比较(断言),从而断定用例是否执行通过。单元测试框架一般会提供丰富的断言方法。例如,判断相等/不等、包含/不包含、True/False 的断言方法等。
● 提供丰富的日志:当测试用例执行失败时能抛出清晰的失败原因,当所有用例执行完成后能提供丰富的执行结果。例如,总执行时间、失败用例数、成功用例数等。
一般的单元测试框架都会提供这些功能,从单元测试框架的这些特性来看,样适用于 Web 自动化用例的开发与执行。
7.1、导出 Junit 自动化测试脚本
你现在一定比较迷惑,如何通过 Junit 单元测试框架来组织 Selenium 自动化测试脚本,或者说如何将这两个技术组合起来做自动化测试。我们可以将 Selenium IDE 编写导出为所需要的格式,从而帮助我们理解自动化脚本的编写。
首先,通过 Selenium IDE 录制一个测试用例,选择菜单栏“文件”-->“Export Test Case As...”弹出二级菜单,这里列举了Selenium IDE所支持导出的编程语言/测试框架/WebDriver(或Remote Control),如下图所示:
Selenium IDE 所支持的导出类型:
Selenium IDE 提供多语言与测试框架的自动化脚本生成,对于学习不同编程语言下的自动化测试脚本开发提供很好的帮助和参考。
因为我们当前使用的自动化测试开发语言为 Java,单元测试框架为 Junit4,自动测试框架为 WebDriver,所以选择“Java/Junit4/WebDriver”选项,将脚本保存到指定位置。
在导出的类型中相比它其语言多了一个选项,叫“Java/Junit4/ WebDriver Backed”选项,它是一种混合的写法,允许让你用 WebDriver 的方式去运行 Remote Control 的脚本。
用MyEclipse2014 打开的导出文件如下:
package com.mypro.unit;
import java.util.concurrent.TimeUnit;
import org.junit.*; //首先要引入 Junit 框架。
import static org.junit.Assert.*;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
public class WebDriver {
// 初始化定义私有变量 driver 为驱动,baseUrl 为基本 URL 地址,acceptNextAlert
// 表示是否继续接受下一个警告,初始化状态为 true。verificationErrors
// 用来存放错误的字符串变量。
private FirefoxDriver driver;
private String baseUrl;
private boolean acceptNextAlert = true;
private StringBuffer verificationErrors = new StringBuffer();
@Before
public void setUp() throws Exception {
// setUp()用于设置初始化工作,在每一个测试用例前先被执行,它与 tearDown()方法相呼应,
// 后者在每一个测试用例执行后被执行。这里的初始化工作定义了浏览器驱动和基础 URL 地址,
//并且还设定的浏览器的超时时间为 30 秒。
driver = new FirefoxDriver();
baseUrl = "https://www.baidu.com/";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
@Test
public void testWebdriver() throws Exception {
//testWebdriver 中放置的就是我们前面一直练习的测试脚本,这部分我们并不陌生,这里就不再解释。
driver.get(baseUrl + "/");
driver.findElement(By.id("kw")).clear();
driver.findElement(By.id("kw")).sendKeys("selenium");
driver.findElement(By.id("su")).click();
}
@After
public void tearDown() throws Exception {
/**
* tearDown()方法在每个测试方法执行后调用,这个方法用于完成测试用例执行后的清理工作,
* 如退出浏览器、关闭驱动,恢复用例执行前的状态等。在 setUp()方法中定义了
* verificationErrors 为空字符串,这里通过 assertEqual()比较其是否为空,
* 如果为空说明用例执行的过程过程中没有出现异常,否则不为空将抛出 AssertionError 异常。
*/
// @Before 注释的方法会在每个用例之前执行。
// @Test 注释的方法表示一个测试用例。
// @After 注释的方法会在每个用例后执行。
driver.quit();
String verificationErrorString = verificationErrors.toString();
if (!"".equals(verificationErrorString)) {
fail(verificationErrorString);
}
}
private boolean isElementPresent(By by) {
//isElementPresent()方法用来查找页面元素是否存在,通过 findElement()来接收元素的定位,
//如果能找到元素返回 true,否则出现异常并返回 flase。try...except....为 Java 语言的异处理
try {
driver.findElement(by);
return true;
} catch (NoSuchElementException e) {
return false;
}
}
private boolean isAlertPresent() {
//isAlertPresent()方法用于判断当前页面是否存在警告框,利用 WebDriver 所提供的
//switchTo().alert()方法来捕捉页面上警告框。如果捕捉到警告框则返回 True,
//否则将抛出 NoAlertPresentException 类型异常,并返回 Flase。
/**
* 不过,经过我的验证,不管页面是否出现警告框,返回结果都为 True。所以,可以调整该方法为
* switchTo().alert().getText(),用于获取当前页面上的警告提示信息。
* 可以获取到将会返回 True,获取不到则返回 False.
*/
try {
driver.switchTo().alert();
return true;
} catch (NoAlertPresentException e) {
return false;
}
}
private String closeAlertAndGetItsText() {
/**
* closeAlertAndGetItsText()关闭警告并且获得警告信息。首先通过 switchTo().alert()获得警告,
* 通过.text获得警告框信息。接着通过 if 语句判断 acceptNextAlert 的状态,
* 在初始化私有变量的时候状态为 True,如果为 True,通过 accept()接受警告。否则 dismiss()忽略此警告。
*/
try {
Alert alert = driver.switchTo().alert();
String alertText = alert.getText();
if (acceptNextAlert) {
alert.accept();
} else {
alert.dismiss();
}
return alertText;
} finally {
acceptNextAlert = true;
}
}
}