Selenium Web 自动化 - 项目实战(一)

时间:2021-12-14 08:54:28

Selenium Web 自动化 - 测试框架(一)

2016-08-05

 

目录

1 框架结构雏形
2 把Java项目转变成Maven项目
3 加入TestNG配置文件
4 Eclipse编码修改
5 编写代码
  5.1 封装Selenium操作
  5.2 使用log4j进行日志输出
  5.3 封装测试平台和测试浏览器选择工具类
  5.4 根据key读取属性文件里面的value值
  5.5 arrow插件解析
    5.5.1 ConfigReader.java
    5.5.2 负责监听测试运行状态和结果
    5.5.3 负责失败的用例重跑的监听器
    5.5.4 负责生成测试报告的监听器
  5.6 用例的开头和结尾设计
  5.7 页面类设计
  5.8 页面帮助类设计
  5.9 书写第一个用例
  5.10 完整的pom.xml和testng.xml
6 配置测试报告目录
7 填加driver
8 执行用例

 源代码

自动化项目由maven+TestNG+selenium设计而成。

  • maven:是一个项目管理工具,主要用于项目构建,依赖管理,项目信息管理。这里主要用到它的jar包管理
  • TestNG:是一套根据JUnit 和NUnit思想而构建的利用注释来强化测试功能的一个测试框架,即可以用来做单元测试,也可以用来做集成测试。

1 框架结构雏形


 返回

新建的一个java project,项目名为autotest,创建如下结构

Selenium Web 自动化 - 项目实战(一)

图1 框架结构雏形

  • base:里面有个基类 (BaseParpare.java),这个类的主要作用是启动(启动浏览器使用了TetsNG的@BeforeClass)和关闭浏览器的作用(关闭浏览器使用了TetsNG的@AftereClass)
  • pages:页面元素类,每一个java类,都是一个页面,存放的都是对应页面的所有元素定位值。
  • pageshelper:这个包主要存放的是对应页面的帮助类
  • plugins:主要存放的是arrow插件以及第三方插件。
  • testcases:顾名思义就是存放测试用例的地方
  • utils:这个包主要是封装了各种工具类,Selenium api封装类,读取属性文件类和选择浏览器类等
  • config目录:存储框架类所需的所有属性文件,比如arrow的配置文件以及自定义的一些属性文件。
  • res目录:主要存放的是driver
  • result目录:存储测试框架运行测试用例生成的报告(包含log,截图等)

2 把Java项目转变成Maven项目


 返回

右击项目->configure->covert to maven project,修改属性groupId=com.demo,

Selenium Web 自动化 - 项目实战(一)

图2 Creat new POM

生成pom.xml如下所示:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>autotest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
View Code

其中:

  • Group Id:填写你组织机构,比如我这里就写的com.demo,例如要是百度可以写成com.baidu
  • Artifact Id:可以理解成为项目打包成jar包的ID 或者说 jar包名字,一般以项目名命名。

3 加入TestNG配置文件 


 返回

右击项目->TestNG->Convert to TestNG,生成testng.xml如下所示:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Test">
</test> <!-- Test -->
</suite> <!-- Suite -->
View Code

4 Eclipse编码修改


 返回

为了统一编码和以后为了避免乱码的出现,我们整个环境都使用UTF-8编码模式

Window-> Preferences,在弹出窗口Preferences中general->workspace, text file encoding 选择other 选项为UTF-8

5 编写代码


 返回

名称 描述 链接
Selenium Web 自动化 - 项目实战(一)  这个类的主要作用是启动和关闭浏览器  5.6
Selenium Web 自动化 - 项目实战(一)

 这个包存放的都是对应页面的所有元素

 5.7
Selenium Web 自动化 - 项目实战(一)  这个包主要存放的是对应页面的帮助类  5.8
Selenium Web 自动化 - 项目实战(一)  主要存放的是arrow插件以及第三方插件  5.5
Selenium Web 自动化 - 项目实战(一) 存放测试用例的地方。在这个包下,还会有很多子包,子包的个数根据你测试的系统的模块来划分  5.9
Selenium Web 自动化 - 项目实战(一) 这个类配置了log  5.2
Selenium Web 自动化 - 项目实战(一)    5.4
Selenium Web 自动化 - 项目实战(一)  封装测试平台和测试浏览器选择工具类  5.3
Selenium Web 自动化 - 项目实战(一)  这个类封装了Selenium API  5.1
Selenium Web 自动化 - 项目实战(一)  Java Application默认带的library  
Selenium Web 自动化 - 项目实战(一)  项目依赖的jar  
Selenium Web 自动化 - 项目实战(一)  项目编译生成class文件存放目录,Eclipse会自动控制。  
Selenium Web 自动化 - 项目实战(一) arrow的配置文件  5.5.1
Selenium Web 自动化 - 项目实战(一) driver的配置文件  5.4 
Selenium Web 自动化 - 项目实战(一)  用ie浏览器测试所需要的driver  
Selenium Web 自动化 - 项目实战(一)  存储测试框架运行测试用例生成的报告(包含log,截图等)  
Selenium Web 自动化 - 项目实战(一)  可以不用管,由maven控制即可  
Selenium Web 自动化 - 项目实战(一)  maven的配置文件,项目核心配置,用于构建项目、自动下载项目依赖以及后续的和testng、jenkins配合持续集成等  5.10
Selenium Web 自动化 - 项目实战(一)    5.10

5.1 封装Selenium操作

SeleniumUtil.java:封装一下selenium的api (会调用SelectBrowser.java,后面补上这个类),代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.utils;

import java.io.File;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.NoAlertPresentException;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.ITestContext;
import org.testng.ITestResult;


/**
* @Description 包装所有selenium的操作以及通用方法,简化用例中代码量
*
*/
public class SeleniumUtil {
/** 使用Log4j,第一步就是获取日志记录器,这个记录器将负责控制日志信息 */
public static Logger logger = Logger.getLogger(SeleniumUtil.class.getName());
public ITestResult it = null;
public WebDriver driver = null;
public WebDriver window = null;

/***
* 启动浏览器并打开页面
*
*/
public void launchBrowser(String browserName, ITestContext context,String webUrl,int timeOut) {
SelectBrowser select
= new SelectBrowser();
driver
= select.selectExplorerByName(browserName, context);
try {
maxWindow(browserName);
waitForPageLoading(timeOut);
get(webUrl);
}
catch (TimeoutException e) {
logger.warn(
"注意:页面没有完全加载出来,刷新重试!!");
refresh();
JavascriptExecutor js
= (JavascriptExecutor)driver;
String status
= (String)js.executeScript("return document.readyState");


logger.info(
"打印状态:"+status);
}

}

// ------------------------------- 对窗口进行操作 -----------------------------------
/**
* 最大化浏览器操作
*
*/
public void maxWindow(String browserName) {
logger.info(
"最大化浏览器:" + browserName);
driver.manage().window().maximize();
}

/**
* 设定浏览器窗口大小: 设置浏览器窗口的大小有下面两个比较常见的用途:<br>
* 1、在统一的浏览器大小下运行用例,可以比较容易的跟一些基于图像比对的工具进行结合
* ,提升测试的灵活性及普遍适用性。比如可以跟sikuli结合,使用sikuli操作flash;<br>
* 2、在不同的浏览器大小下访问测试站点,对测试页面截图并保存,然后观察或使用图像比对工具对被测页面的前端样式进行评测。
* 比如可以将浏览器设置成移动端大小(320x480),然后访问移动站点,对其样式进行评估;<br>
*
*/
public void setBrowserSize(int width, int height) {
driver.manage().window().setSize(
new Dimension(width, height));
}

/**
* get方法包装
*
*/
public void get(String url) {
driver.get(url);
logger.info(
"打开测试页面:[" + url + "]");
}

/**
* close方法包装
*
*/
public void close() {
driver.close();
}

/**
* 退出
*
*/
public void quit() {
driver.quit();
}

/**
* 刷新方法包装
*
*/
public void refresh() {
driver.navigate().refresh();
logger.info(
"页面刷新成功!");
}

/**
* 后退方法包装
*
*/
public void back() {
driver.navigate().back();
}

/**
* 前进方法包装
*
*/
public void forward() {
driver.navigate().forward();
}

/**
* 获得页面的标题
*
*/
public String getTitle() {
return driver.getTitle();
}

/**
* 等待alert出现
*
*/
public Alert switchToPromptedAlertAfterWait(long waitMillisecondsForAlert) throws NoAlertPresentException {
final int ONE_ROUND_WAIT = 200;
NoAlertPresentException lastException
= null;

long endTime = System.currentTimeMillis() + waitMillisecondsForAlert;

for (long i = 0; i < waitMillisecondsForAlert; i += ONE_ROUND_WAIT) {

try {
Alert alert
= driver.switchTo().alert();
return alert;
}
catch (NoAlertPresentException e) {
lastException
= e;
}
pause(ONE_ROUND_WAIT);

if (System.currentTimeMillis() > endTime) {
break;
}
}
throw lastException;
}

/**
* @Description 对于windows GUI弹出框,要求输入用户名和密码时,
* seleniumm不能直接操作,需要借助http://modifyusername:modifypassword@yoururl 这种方法
*
*
*/
public void loginOnWinGUI(String username, String password, String url) {
driver.get(username
+ ":" + password + "@" + url);
}

//============================== 对窗口进行操作 ==================================

//------------------------------ 查找元素 -------------------------------------
/**
* 包装查找元素的方法 element
*
*/
public WebElement findElementBy(By by) {
return driver.findElement(by);
}

/**
* 包装查找元素的方法 elements
*
*/
public List<WebElement> findElementsBy(By by) {
return driver.findElements(by);
}

/**
* 这是一堆相同的elements中 选择 其中方的 一个 然后在这个选定的中 继续定位
*
*/
public WebElement getOneElement(By bys, By by, int index) {
return findElementsBy(bys).get(index).findElement(by);
}

//============================= 查找元素 =========================================

//--------------------------- 判断元素状态 ----------------------------------------
/**
* 判断文本是不是和需求要求的文本一致
* *
*/
public void isTextCorrect(String actual, String expected) {
try {
Assert.assertEquals(actual, expected);
}
catch (AssertionError e) {
logger.error(
"期望的文字是 [" + expected + "] 但是找到了 [" + actual + "]");
Assert.fail(
"期望的文字是 [" + expected + "] 但是找到了 [" + actual + "]");

}
logger.info(
"找到了期望的文字: [" + expected + "]");
}

/**
* 判断编辑框是不是可编辑
*
*/
public void isInputEdit(WebElement element) {

}

/** 检查元素是否可用 */
public boolean isEnabled(WebElement element) {
boolean isEnable = false;
if (element.isEnabled()) {
logger.info(
"The element: [" + getLocatorByElement(element, ">") + "] is enabled");
isEnable
= true;
}
else if (element.isDisplayed() == false) {
logger.warn(
"The element: [" + getLocatorByElement(element, ">") + "] is not enable");
isEnable
= false;
}
return isEnable;
}


/** 检查元素是否显示 */
public boolean isDisplayed(WebElement element) {
boolean isDisplay = false;
if (element.isDisplayed()) {
logger.info(
"The element: [" + getLocatorByElement(element, ">") + "] is displayed");
isDisplay
= true;
}
else if (element.isDisplayed() == false) {
logger.warn(
"The element: [" + getLocatorByElement(element, ">") + "] is not displayed");

isDisplay
= false;
}
return isDisplay;
}

/**检查元素是不是存在*/
public boolean isElementExist(By byElement){
try{
findElementBy(byElement);
return true;
}
catch(NoSuchElementException nee){
return false;
}
}

/** 检查元素是否被勾选 */
public boolean isSelected(WebElement element) {
boolean flag = false;
if (element.isSelected() == true) {
logger.info(
"The element: [" + getLocatorByElement(element, ">") + "] is selected");
flag
= true;
}
else if (element.isSelected() == false) {
logger.info(
"The element: [" + getLocatorByElement(element, ">") + "] is not selected");
flag
= false;
}
return flag;
}

/**
* 判断实际文本时候包含期望文本
*
*
@param actual
* 实际文本
*
@param expect
* 期望文本
*/
public void isContains(String actual, String expect) {
try {
Assert.assertTrue(actual.contains(expect));
}
catch (AssertionError e) {
logger.error(
"The [" + actual + "] is not contains [" + expect + "]");
Assert.fail(
"The [" + actual + "] is not contains [" + expect + "]");
}
logger.info(
"The [" + actual + "] is contains [" + expect + "]");
}

/** 检查checkbox是不是勾选 */
public boolean isCheckboxSelected(By elementLocator) {
if (findElementBy(elementLocator).isSelected() == true) {
logger.info(
"CheckBox: " + getLocatorByElement(findElementBy(elementLocator), ">") + " 被勾选");
return true;
}
else
logger.info(
"CheckBox: " + getLocatorByElement(findElementBy(elementLocator), ">") + " 没有被勾选");
return false;

}

/**
* 在给定的时间内去查找元素,如果没找到则超时,抛出异常
*
*/
public void waitForElementToLoad(int timeOut, final By By) {
logger.info(
"开始查找元素[" + By + "]");
try {
(
new WebDriverWait(driver, timeOut)).until(new ExpectedCondition<Boolean>() {

public Boolean apply(WebDriver driver) {
WebElement element
= driver.findElement(By);
return element.isDisplayed();
}
});
}
catch (TimeoutException e) {
logger.error(
"超时!! " + timeOut + " 秒之后还没找到元素 [" + By + "]");
Assert.fail(
"超时!! " + timeOut + " 秒之后还没找到元素 [" + By + "]");

}
logger.info(
"找到了元素 [" + By + "]");
}

/**
* pageLoadTimeout。页面加载时的超时时间。因为webdriver会等页面加载完毕在进行后面的操作,
* 所以如果页面在这个超时时间内没有加载完成,那么webdriver就会抛出异常
*/
public void waitForPageLoading(int pageLoadTime) {
driver.manage().timeouts().pageLoadTimeout(pageLoadTime, TimeUnit.SECONDS);
}
//========================== 判断元素状态 =======================================

//-------------------------- 对元素操作 ----------------------------------------
/**
* 获得元素的文本
*
*/
public String getText(By elementLocator) {
return driver.findElement(elementLocator).getText().trim();
}

/**
* 获得当前select选择的值
*
*/
public List<WebElement> getCurrentSelectValue(By by){
List
<WebElement> options = null;
Select s
= new Select(driver.findElement(by));
options
= s.getAllSelectedOptions();
return options;
}

/**
* 获得输入框的值 这个方法 是针对某些input输入框 没有value属性,但是又想取得input的 值得方法
*
*/
public String getInputValue(String chose,String choseValue) {
String value
= null;
switch(chose.toLowerCase()){
case "name":
String jsName
= "return document.getElementsByName('"+choseValue+"')[0].value;"; //把JS执行的值 返回出去
value = (String)((JavascriptExecutor) driver).executeScript(jsName);
break;

case "id":
String jsId
= "return document.getElementById('"+choseValue+"').value;"; //把JS执行的值 返回出去
value = (String)((JavascriptExecutor) driver).executeScript(jsId);
break;

default:
logger.error(
"未定义的chose:"+chose);
Assert.fail(
"未定义的chose:"+chose);
}
return value;

}

/** 获得CSS value */
public String getCSSValue(WebElement e, String key) {

return e.getCssValue(key);
}

/**
* 获得元素 属性的文本
*
*/
public String getAttributeText(By elementLocator, String attribute) {
return driver.findElement(elementLocator).getAttribute(attribute).trim();
}

/** 根据元素来获取此元素的定位值 */
public String getLocatorByElement(WebElement element, String expectText) {
String text
= element.toString();
String expect
= null;
try {
expect
= text.substring(text.indexOf(expectText) + 1, text.length() - 1);
}
catch (Exception e) {
e.printStackTrace();
logger.error(
"failed to find the string [" + expectText + "]");
}
return expect;
}

/**
* 包装点击操作
*
*/
public void click(By byElement) {

try {
clickTheClickable(byElement, System.currentTimeMillis(),
2500);
}
catch (StaleElementReferenceException e) {
logger.error(
"The element you clicked:[" + byElement + "] is no longer exist!");
Assert.fail(
"The element you clicked:[" + byElement + "] is no longer exist!");
}
catch (Exception e) {
logger.error(
"Failed to click element [" + byElement + "]");
Assert.fail(
"Failed to click element [" + byElement + "]",e);
}
logger.info(
"点击元素 [" + byElement + "]");
}

/**
* 包装清除操作
*
*/
public void clear(By byElement) {
try {
findElementBy(byElement).clear();
}
catch (Exception e) {
logger.error(
"清除元素 [" + byElement + "] 上的内容失败!");
}
logger.info(
"清除元素 [" + byElement + "]上的内容");
}

/**
* 向输入框输入内容
*
*/
public void type(By byElement, String key) {
try {
findElementBy(byElement).sendKeys(key);
}
catch (Exception e) {
e.printStackTrace();
logger.error(
"输入 [" + key + "] 到 元素[" + byElement + "]失败");
Assert.fail(
"输入 [" + key + "] 到 元素[" + byElement + "]失败");
}
logger.info(
"输入:[" + key + "] 到 [" + byElement + "]");
}

/**
* 模拟键盘操作的,比如Ctrl+A,Ctrl+C等 参数详解:<br>
* 1、WebElement element - 要被操作的元素 <br>
* 2、Keys key- 键盘上的功能键 比如ctrl ,alt等 <br>
* 3、String keyword - 键盘上的字母
*
*/
public void pressKeysOnKeyboard(WebElement element, Keys key, String keyword) {

element.sendKeys(Keys.chord(key, keyword));
}

/**
* 切换frame - 根据String类型(frame名字)
*
*/
public void inFrame(String frameId) {
driver.switchTo().frame(frameId);
}

/**
* 切换frame - 根据frame在当前页面中的顺序来定位
*
*/
public void inFrame(int frameNum) {
driver.switchTo().frame(frameNum);
}

/**
* 切换frame - 根据页面元素定位
*
*/
public void switchFrame(WebElement element) {
try {
logger.info(
"正在跳进frame:[" + getLocatorByElement(element, ">") + "]");
driver.switchTo().frame(element);
}
catch (Exception e) {
logger.info(
"跳进frame: [" + getLocatorByElement(element, ">") + "] 失败");
Assert.fail(
"跳进frame: [" + getLocatorByElement(element, ">") + "] 失败");
}
logger.info(
"进入frame: [" + getLocatorByElement(element, ">") +"]成功 ");
}

/** 跳出frame */
public void outFrame() {
driver.switchTo().defaultContent();
}

/**
* 选择下拉选项 -根据value
*
*/
public void selectByValue(By by, String value) {
Select s
= new Select(driver.findElement(by));
s.selectByValue(value);
}

/**
* 选择下拉选项 -根据index角标
*
*/
public void selectByIndex(By by, int index) {
Select s
= new Select(driver.findElement(by));
s.selectByIndex(index);
}

/**
* 选择下拉选项 -根据文本内容
*
*/
public void selectByText(By by, String text) {
Select s
= new Select(driver.findElement(by));
s.selectByVisibleText(text);
}

/**
* 执行JavaScript 方法
*
*/
public void executeJS(String js) {
((JavascriptExecutor) driver).executeScript(js);
logger.info(
"执行JavaScript语句:[" + js + "]");
}

/**
* 执行JavaScript 方法和对象
* 用法:seleniumUtil.executeJS("arguments[0].click();", seleniumUtil.findElementBy(MyOrdersPage.MOP_TAB_ORDERCLOSE));
*
*/
public void executeJS(String js, Object... args) {
((JavascriptExecutor) driver).executeScript(js, args);
logger.info(
"执行JavaScript语句:[" + js + "]");
}

/**
* 包装selenium模拟鼠标操作 - 鼠标移动到指定元素
*
*/
public void mouseMoveToElement(By by) {
Actions builder
= new Actions(driver);
Actions mouse
= builder.moveToElement(driver.findElement(by));
mouse.perform();
}

/**
* 包装selenium模拟鼠标操作 - 鼠标移动到指定元素
*
*/
public void mouseMoveToElement(WebElement element) {
Actions builder
= new Actions(driver);
Actions mouse
= builder.moveToElement(element);
mouse.perform();
}

/**
* 包装selenium模拟鼠标操作 - 鼠标右击
*
*/
public void mouseRightClick(By element) {
Actions builder
= new Actions(driver);
Actions mouse
= builder.contextClick(findElementBy(element));
mouse.perform();
}

/**
* 上传文件,需要点击弹出上传照片的窗口才行
*
*
@param brower
* 使用的浏览器名称
*
@param file
* 需要上传的文件及文件名
*/
public void handleUpload(String browser, File file) {
String filePath
= file.getAbsolutePath();
String executeFile
= "res/script/autoit/Upload.exe";
String cmd
= "\"" + executeFile + "\"" + " " + "\"" + browser + "\"" + " " + "\"" + filePath + "\"";
try {
Process p
= Runtime.getRuntime().exec(cmd);
p.waitFor();
}
catch (Exception e) {
e.printStackTrace();
}
}
// ===================== 对元素进行操作 ============================

/**
* 添加cookies,做自动登陆的必要方法
*
*/
public void addCookies(int sleepTime) {
pause(sleepTime);
Set
<Cookie> cookies = driver.manage().getCookies();
for (Cookie c : cookies) {
System.out.println(c.getName()
+ "->" + c.getValue());
if (c.getName().equals("logisticSessionid")) {
Cookie cook
= new Cookie(c.getName(), c.getValue());
driver.manage().addCookie(cook);
System.out.println(c.getName()
+ "->" + c.getValue());
System.out.println(
"添加成功");
}
else {
System.out.println(
"没有找到logisticSessionid");
}

}

}

// webdriver中可以设置很多的超时时间
/** implicitlyWait。识别对象时的超时时间。过了这个时间如果对象还没找到的话就会抛出NoSuchElement异常 */
public void implicitlyWait(int timeOut) {
driver.manage().timeouts().implicitlyWait(timeOut, TimeUnit.SECONDS);
}

/** setScriptTimeout。异步脚本的超时时间。webdriver可以异步执行脚本,这个是设置异步执行脚本脚本返回结果的超时时间 */
public void setScriptTimeout(int timeOut) {
driver.manage().timeouts().setScriptTimeout(timeOut, TimeUnit.SECONDS);
}

/** 获得屏幕的分辨率 - 宽 */
public static double getScreenWidth() {
return java.awt.Toolkit.getDefaultToolkit().getScreenSize().getWidth();
}


/**
* 暂停当前用例的执行,暂停的时间为:sleepTime
*
*/
public void pause(int sleepTime) {
if (sleepTime <= 0) {
return;
}
try {
Thread.sleep(sleepTime);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}

/** 不能点击时候重试点击操作 */
private void clickTheClickable(By byElement, long startTime, int timeOut) throws Exception {
try {
findElementBy(byElement).click();
}
catch (Exception e) {
if (System.currentTimeMillis() - startTime > timeOut) {
logger.warn(byElement
+ " is unclickable");
throw new Exception(e);
}
else {
Thread.sleep(
500);
logger.warn(byElement
+ " is unclickable, try again");
clickTheClickable(byElement, startTime, timeOut);
}
}
}

}
View Code

pom.xml中添加代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
        <dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
<version>LATEST</version>
</dependency>
View Code

5.2 使用log4j进行日志输出

LogConfiguration.java:配置了log的输出等级,以及如何显示,如何输出,输出的日志保存到哪里等配置,代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.utils;
import java.util.Properties;
import org.apache.log4j.PropertyConfigurator;
/**
*
@author young
* @decription 动态生成各个模块中的每条用例的日志,运行完成用例之后请到result/log目录下查看
*
*/
public class LogConfiguration {

public static void initLog(String fileName){
//获取到模块名字
String founctionName = getFunctionName(fileName);
//声明日志文件存储路径以及文件名、格式
final String logFilePath = "./result/log/"+founctionName+"/"+fileName+".log";
Properties prop
= new Properties();
//配置日志输出的格式
prop.setProperty("log4j.rootLogger","info, toConsole, toFile");
prop.setProperty(
"log4j.appender.file.encoding","UTF-8" );
prop.setProperty(
"log4j.appender.toConsole","org.apache.log4j.ConsoleAppender");
prop.setProperty(
"log4j.appender.toConsole.Target","System.out");
prop.setProperty(
"log4j.appender.toConsole.layout","org.apache.log4j.PatternLayout ");
prop.setProperty(
"log4j.appender.toConsole.layout.ConversionPattern","[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n");
prop.setProperty(
"log4j.appender.toFile", "org.apache.log4j.DailyRollingFileAppender");
prop.setProperty(
"log4j.appender.toFile.file", logFilePath);
prop.setProperty(
"log4j.appender.toFile.append", "false");
prop.setProperty(
"log4j.appender.toFile.Threshold", "info");
prop.setProperty(
"log4j.appender.toFile.layout", "org.apache.log4j.PatternLayout");
prop.setProperty(
"log4j.appender.toFile.layout.ConversionPattern", "[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n");
//使配置生效
PropertyConfigurator.configure(prop);

}


/**取得模块名字*/
public static String getFunctionName(String fileName){
String functionName
= null;
int firstUndelineIndex = fileName.indexOf("_");
functionName
= fileName.substring(0, firstUndelineIndex-4);
return functionName;

}


}
View Code

5.3 封装测试平台和测试浏览器选择工具类

SelectBrowser.java:(会调用PropertiesDataProvider.java,后面补上这个类),代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.utils;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.Assert;
import org.testng.ITestContext;

/**
*
@author young
* @decription 在不同的平台上选择对应的浏览器,系统平台程序自动判断是什么平台
*
*/
public class SelectBrowser {
static Logger logger = Logger.getLogger(SelectBrowser.class.getName());

public WebDriver selectExplorerByName(String browser, ITestContext context) {
Properties props
= System.getProperties(); // 获得系统属性集
String currentPlatform = props.getProperty("os.name"); // 操作系统名称
logger.info("当前操作系统是:[" + currentPlatform + "]");
logger.info(
"启动测试浏览器:[" + browser + "]");
//从testNG的配置文件读取参数driverConfgFilePath的值
String driverConfgFilePath = context.getCurrentXmlTest().getParameter("driverConfgFilePath");
/** 声明好驱动的路径 */
String chromedriver_win
= PropertiesDataProvider.getTestData(driverConfgFilePath, "chromedriver_win");
String chromedriver_linux
= PropertiesDataProvider.getTestData(driverConfgFilePath, "chromedriver_linux");
String chromedriver_mac
= PropertiesDataProvider.getTestData(driverConfgFilePath, "chromedriver_mac");
String ghostdriver_win
= PropertiesDataProvider.getTestData(driverConfgFilePath, "ghostdriver_win");
String iedriver
= PropertiesDataProvider.getTestData(driverConfgFilePath, "iedriver");
if (currentPlatform.toLowerCase().contains("win")) { //如果是windows平台

if (browser.equalsIgnoreCase("ie")) {
System.setProperty(
"webdriver.ie.driver", iedriver);
//IE的常规设置,便于执行自动化测试
DesiredCapabilities ieCapabilities = DesiredCapabilities.internetExplorer();
ieCapabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,
true);
//返回ie浏览器对象
return new InternetExplorerDriver(ieCapabilities);
}
else if (browser.equalsIgnoreCase("chrome")) {
System.setProperty(
"webdriver.chrome.driver", chromedriver_win);
//返回谷歌浏览器对象
return new ChromeDriver();
}
else if (browser.equalsIgnoreCase("firefox")) {
//返回火狐浏览器对象
return new FirefoxDriver();

}
else if(browser.equalsIgnoreCase("ghost")){
DesiredCapabilities ghostCapabilities
= new DesiredCapabilities();
ghostCapabilities.setJavascriptEnabled(
true);
ghostCapabilities.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, ghostdriver_win);
//返回ghost对象
return new PhantomJSDriver(ghostCapabilities);

}
else {

logger.error(
"The [" + browser + "]" + " explorer is not applicable for [" + currentPlatform + "] OS");
Assert.fail(
"The [" + browser + "]" + " explorer does not apply to [" + currentPlatform + "] OS");

}

}
else if (currentPlatform.toLowerCase().contains("linux")) { //如果是linux平台

if (browser.equalsIgnoreCase("chrome")) {
System.setProperty(
"webdriver.chrome.driver", chromedriver_linux);
return new ChromeDriver();

}
else if (browser.equalsIgnoreCase("firefox")) {
return new FirefoxDriver();
}
else {
logger.error(
"The [" + browser + "]" + " explorer does not apply to [" + currentPlatform + "] OS");
Assert.fail(
"The [" + browser + "]" + " explorer does not apply to [" + currentPlatform + "] OS");
}

}
else if (currentPlatform.toLowerCase().contains("mac")) { //如果是mac平台
if (browser.equalsIgnoreCase("chrome")) {
System.setProperty(
"webdriver.chrome.driver", chromedriver_mac);
return new ChromeDriver();
}
else if (browser.equalsIgnoreCase("firefox")) {
return new FirefoxDriver();
}
else {
logger.error(
"The [" + browser + "]" + " explorer does not apply to [" + currentPlatform + "] OS");
Assert.fail(
"The [" + browser + "]" + " explorer does not apply to [" + currentPlatform + "] OS");
}

}
else
logger.error(
"The [" + currentPlatform + "] is not supported for this automation frame,please change the OS(Windows,MAC or LINUX)");

Assert.fail(
"The [" + currentPlatform + "] is not supported for this automation frame,please change the OS(Windows,MAC or LINUX)");
return null;
}
}
View Code

pom.xml中添加代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
        <dependency>
<groupId>com.codeborne</groupId>
<artifactId>phantomjsdriver</artifactId>
<version>1.2.1</version>
</dependency>
View Code

testng.xml中添加代码如下:

    <parameter name="driverConfgFilePath" value="config/driver.properties" />

5.4 根据key读取属性文件里面的value值

PropertiesDataProvider.java:从.properties文件中读取相关测试数据,代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.utils;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;

/**
* @Desription 从.properties文件中读取相关测试数据<br>
*
*
*/
public class PropertiesDataProvider {

public static String getTestData(String configFilePath, String key) {
Configuration config
= null;
try {
config
= new PropertiesConfiguration(configFilePath);
}
catch (ConfigurationException e) {
e.printStackTrace();
}
return String.valueOf(config.getProperty(key));

}
}
View Code

driver.properties代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
# define the browser driver here 
#chrome driver for windows
chromedriver_win =res/driver/chrome/win/chromedriver.exe
#chrome driver for linux
chomedriver_linux =res/driver/chrome/linux/chromedriver
#chrome driver for mac os
chomedriver_mac = res/driver/chrome/mac/chromedriver
#chrome driver for IE
iedriver = res/driver/ie/iedriver.exe
#ghost driver for windows
ghostdriver_win =res/driver/ghostdriver/phantomjs.exe
View Code

5.5 arrow插件解析

5.5.1 ConfigReader.java

代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.plugins.arrow.utils;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;
import org.testng.log4testng.Logger;

public class ConfigReader {
private static Logger logger = Logger.getLogger(ConfigReader.class);
private static ConfigReader cr;
private int retryCount = 0;
private String sourceCodeDir = "src";
private String sourceCodeEncoding = "UTF-8";
private static final String RETRYCOUNT = "retrycount";
private static final String SOURCEDIR = "sourcecodedir";
private static final String SOURCEENCODING = "sourcecodeencoding";
private static final String CONFIGFILE = "./config/config.properties";

private ConfigReader() {
readConfig(CONFIGFILE);
}

public static ConfigReader getInstance() {
if (cr == null) {
cr
= new ConfigReader();
}
return cr;
}

private void readConfig(String fileName) {
Properties properties
= getConfig(fileName);
if (properties != null) {
String sRetryCount
= null;

Enumeration
<?> en = properties.propertyNames();
while (en.hasMoreElements()) {
String key
= (String) en.nextElement();
if (key.toLowerCase().equals(RETRYCOUNT)) {
sRetryCount
= properties.getProperty(key);
}
if (key.toLowerCase().equals(SOURCEDIR)) {
sourceCodeDir
= properties.getProperty(key);
}
if (key.toLowerCase().equals(SOURCEENCODING)) {
sourceCodeEncoding
= properties.getProperty(key);
}
}
if (sRetryCount != null) {
sRetryCount
= sRetryCount.trim();
try {
retryCount
= Integer.parseInt(sRetryCount);
}
catch (final NumberFormatException e) {
throw new NumberFormatException("Parse " + RETRYCOUNT + " [" + sRetryCount + "] from String to Int Exception");
}
}
}
}

public int getRetryCount() {
return this.retryCount;
}

public String getSourceCodeDir() {
return this.sourceCodeDir;
}

public String getSrouceCodeEncoding() {
return this.sourceCodeEncoding;
}

/**
*
*
@param propertyFileName
*
*
@return
*/
private Properties getConfig(String propertyFileName) {
Properties properties
= new Properties();
try {
properties.load(
new FileInputStream(propertyFileName));
}
catch (FileNotFoundException e) {
properties
= null;
logger.warn(
"FileNotFoundException:" + propertyFileName);
}
catch (IOException e) {
properties
= null;
logger.warn(
"IOException:" + propertyFileName);
}
return properties;
}
}
View Code

config.properties代码如下:

retrycount=0
sourcecodedir=src/com/demo/test/testcases
sourcecodeencoding=UTF-8

5.5.2 负责监听测试运行状态和结果

TestResultListener.java代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.plugins.arrow;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.TestListenerAdapter;

/**
*
@author netease_arrow 描述:来自网易的截图插件
*
*/
public class TestResultListener extends TestListenerAdapter {

private static Logger logger = Logger.getLogger(TestResultListener.class.getName());
protected ITestContext testContext = null; // 这里也是新加的
String browser = null;

@Override
public void onStart(ITestContext testContext) { // 这里也是新加的,用于对context进行统一
this.testContext = testContext;
browser
= String.valueOf(testContext.getCurrentXmlTest().getParameter("browserName"));
super.onStart(testContext);
}

@Override
public void onTestFailure(ITestResult tr) {
super.onTestFailure(tr);
logger.warn(tr.getName()
+ " 测试用例执行失败!");
WebDriver webDriver
= (WebDriver) testContext.getAttribute("SELENIUM_DRIVER"); // 这里就是取driver
saveScreenShot(tr, webDriver,browser);
}

@Override
public void onTestSkipped(ITestResult tr) {
super.onTestSkipped(tr);
WebDriver webDriver
= (WebDriver) testContext.getAttribute("SELENIUM_DRIVER");
logger.warn(tr.getName()
+ " 测试用例由于某些原因被跳过!");
saveScreenShot(tr, webDriver,browser);

}

@Override
public void onTestSuccess(ITestResult tr) {
super.onTestSuccess(tr);
logger.info(tr.getName()
+ " 测试用例执行成功!");
}

@Override
public void onTestStart(ITestResult tr) {
super.onTestStart(tr);
logger.info(tr.getName()
+ " 测试用例开始执行!");
}

@Override
public void onFinish(ITestContext testContext) {
super.onFinish(testContext);

// List of test results which we will delete later
ArrayList<ITestResult> testsToBeRemoved = new ArrayList<ITestResult>();
// collect all id's from passed test
Set<Integer> passedTestIds = new HashSet<Integer>();
for (ITestResult passedTest : testContext.getPassedTests().getAllResults()) {
logger.info(
"执行成功的用例 = " + passedTest.getName());
passedTestIds.add(getId(passedTest));
}

// Eliminate the repeat methods
Set<Integer> skipTestIds = new HashSet<Integer>();
for (ITestResult skipTest : testContext.getSkippedTests().getAllResults()) {
logger.info(
"被跳过的用例 = " + skipTest.getName());
// id = class + method + dataprovider
int skipTestId = getId(skipTest);

if (skipTestIds.contains(skipTestId) || passedTestIds.contains(skipTestId)) {
testsToBeRemoved.add(skipTest);
}
else {
skipTestIds.add(skipTestId);
}
}

// Eliminate the repeat failed methods
Set<Integer> failedTestIds = new HashSet<Integer>();
for (ITestResult failedTest : testContext.getFailedTests().getAllResults()) {
logger.info(
"执行失败的用例 = " + failedTest.getName());
// id = class + method + dataprovider
int failedTestId = getId(failedTest);

// if we saw this test as a failed test before we mark as to be
// deleted
// or delete this failed test if there is at least one passed
// version
if (failedTestIds.contains(failedTestId) || passedTestIds.contains(failedTestId) || skipTestIds.contains(failedTestId)) {
testsToBeRemoved.add(failedTest);
}
else {
failedTestIds.add(failedTestId);
}
}

// finally delete all tests that are marked
for (Iterator<ITestResult> iterator = testContext.getFailedTests().getAllResults().iterator(); iterator.hasNext();) {
ITestResult testResult
= iterator.next();
if (testsToBeRemoved.contains(testResult)) {
logger.info(
"移除重复失败的用例 = " + testResult.getName());
iterator.remove();
}
}

}

private int getId(ITestResult result) {
int id = result.getTestClass().getName().hashCode();
id
= id + result.getMethod().getMethodName().hashCode();
id
= id + (result.getParameters() != null ? Arrays.hashCode(result.getParameters()) : 0);
return id;
}

private void saveScreenShot(ITestResult tr, WebDriver driver, String browser) {
SimpleDateFormat formatter
= new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
String mDateTime
= formatter.format(new Date());
String fileName
= mDateTime + "_" + tr.getName();
String filePath
= "";
try {
// 这里可以调用不同框架的截图功能
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
filePath
= "result/screenshot/"+ fileName + ".jpg";
File destFile
= new File(filePath);
FileUtils.copyFile(screenshot, destFile);
logger.info(
"["+fileName + "] 截图成功,保存在:" + "[ " + filePath + " ]");

}
catch (Exception e) {
filePath
= "["+fileName+"]" + " ,截图失败,原因:" + e.getMessage();
logger.error(filePath);
}

if (!"".equals(filePath)) {
Reporter.setCurrentTestResult(tr);
Reporter.log(filePath);
// 把截图写入到Html报告中方便查看
Reporter.log("<img src=\"../../" + filePath + "\"/>");

}
}

}
View Code

5.5.3 负责失败的用例重跑的监听器

RetryListener.java: (会调用TestngRetry.java,,后面补上这个类),代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.plugins.arrow;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.IRetryAnalyzer;
import org.testng.annotations.ITestAnnotation;

public class RetryListener implements IAnnotationTransformer {

@SuppressWarnings(
"rawtypes")
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
IRetryAnalyzer retry
= annotation.getRetryAnalyzer();
if (retry == null) {
annotation.setRetryAnalyzer(TestngRetry.
class);
}
}
}
View Code

TestngRetry.java代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.plugins.arrow;
import org.apache.log4j.Logger;
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
import org.testng.Reporter;
import com.demo.test.plugins.arrow.utils.ConfigReader;
import com.demo.test.utils.LogConfiguration;

public class TestngRetry implements IRetryAnalyzer {
static {
LogConfiguration.initLog(
"TestngRetryPage_");
}
private static Logger logger = Logger.getLogger(TestngRetry.class);
private int retryCount = 1;
private static int maxRetryCount;

static {
ConfigReader config
= ConfigReader.getInstance();
maxRetryCount
= config.getRetryCount();
logger.info(
"RetryCount=" + maxRetryCount);
logger.info(
"SourceDir=" + config.getSourceCodeDir());
logger.info(
"SourceEncoding=" + config.getSrouceCodeEncoding());
}

public boolean retry(ITestResult result) {
if (retryCount <= maxRetryCount) {
String message
= "Retry for: [" + result.getName() + "] on class [" + result.getTestClass().getName() + "] retry " + retryCount + " times";
logger.info(message);
Reporter.setCurrentTestResult(result);
Reporter.log(
"RunCount=" + (retryCount + 1));
retryCount
++;
return true;
}
return false;

}

public static int getMaxRetryCount() {
return maxRetryCount;
}

public int getRetryCount() {
return retryCount;
}
}
View Code

5.5.4 负责生成测试报告的监听器

PowerEmailableReporter.java:代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.plugins.arrow;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.testng.IInvokedMethod;
import org.testng.IReporter;
import org.testng.IResultMap;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestClass;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.collections.Lists;
import org.testng.internal.Utils;
import org.testng.log4testng.Logger;
import org.testng.xml.XmlSuite;

import com.demo.test.plugins.arrow.utils.ConfigReader;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;


/**
* Reported designed to render self-contained HTML top down view of a testing
* suite.
*/
public class PowerEmailableReporter implements IReporter {
private static final Logger L = Logger.getLogger(PowerEmailableReporter.class);

// ~ Instance fields ------------------------------------------------------

private PrintWriter m_out;

private int m_row;

private Integer m_testIndex;

private Set<Integer> testIds = new HashSet<Integer>();
private List<Integer> allRunTestIds = new ArrayList<Integer>();
private JavaDocBuilder builder = new JavaDocBuilder();

// ~ Methods --------------------------------------------------------------

/** Creates summary of the run */
public void generateReport(List<XmlSuite> xml, List<ISuite> suites, String outdir) {
try {
m_out
= createWriter(outdir);
}
catch (IOException e) {
L.error(
"output file", e);
return;
}
ConfigReader cr
= ConfigReader.getInstance();
builder.setEncoding(cr.getSrouceCodeEncoding());
builder.addSourceTree(
new File(cr.getSourceCodeDir()));
startHtml(m_out);
generateSuiteSummaryReport(suites);
testIds.clear();
generateMethodSummaryReport(suites);
testIds.clear();
generateMethodDetailReport(suites);
testIds.clear();
endHtml(m_out);
m_out.flush();
m_out.close();
}

protected PrintWriter createWriter(String outdir) throws IOException {
new File(outdir).mkdirs();
return new PrintWriter(new BufferedWriter(new FileWriter(new File(outdir, "power-emailable-report.html"))));
}

/**
* Creates a table showing the highlights of each test method with links to
* the method details
*/
protected void generateMethodSummaryReport(List<ISuite> suites) {
startResultSummaryTable(
"methodOverview");
int testIndex = 1;
for (ISuite suite : suites) {
if (suites.size() > 1) {
titleRow(suite.getName(),
5);
}
Map
<String, ISuiteResult> r = suite.getResults();
for (ISuiteResult r2 : r.values()) {
ITestContext testContext
= r2.getTestContext();
String testName
= testContext.getName();
m_testIndex
= testIndex;

resultSummary(suite, testContext.getSkippedConfigurations(), testName,
"skipped", " (configuration methods)");
resultSummary(suite, testContext.getSkippedTests(), testName,
"skipped", "");
resultSummary(suite, testContext.getFailedConfigurations(), testName,
"failed", " (configuration methods)");
resultSummary(suite, testContext.getFailedTests(), testName,
"failed", "");
resultSummary(suite, testContext.getPassedTests(), testName,
"passed", "");

testIndex
++;
}
}
m_out.println(
"</table>");
}

/** Creates a section showing known results for each method */
protected void generateMethodDetailReport(List<ISuite> suites) {
for (ISuite suite : suites) {
Map
<String, ISuiteResult> r = suite.getResults();
for (ISuiteResult r2 : r.values()) {
ITestContext testContext
= r2.getTestContext();
if (r.values().size() > 0) {
m_out.println(
"<h1>" + testContext.getName() + "</h1>");
}
resultDetail(testContext.getFailedConfigurations());
resultDetail(testContext.getFailedTests());
resultDetail(testContext.getSkippedConfigurations());
resultDetail(testContext.getSkippedTests());
resultDetail(testContext.getPassedTests());
}
}
}

/**
*
@param tests
*/
private void resultSummary(ISuite suite, IResultMap tests, String testname, String style, String details) {
if (tests.getAllResults().size() > 0) {
StringBuffer buff
= new StringBuffer();
String lastClassName
= "";
int mq = 0;
int cq = 0;
Map
<String, Integer> methods = new HashMap<String, Integer>();
Set
<String> setMethods = new HashSet<String>();
for (ITestNGMethod method : getMethodSet(tests, suite)) {
m_row
+= 1;

ITestClass testClass
= method.getTestClass();
String className
= testClass.getName();
if (mq == 0) {
String id
= (m_testIndex == null ? null : "t" + Integer.toString(m_testIndex));
titleRow(testname
+ " &#8212; " + style + details, 5, id);
m_testIndex
= null;
}
if (!className.equalsIgnoreCase(lastClassName)) {
if (mq > 0) {
cq
+= 1;
m_out.print(
"<tr class=\"" + style + (cq % 2 == 0 ? "even" : "odd") + "\">" + "<td");
if (mq > 1) {
m_out.print(
" rowspan=\"" + mq + "\"");
}
m_out.println(
">" + lastClassName + "</td>" + buff);
}
mq
= 0;
buff.setLength(
0);
lastClassName
= className;
}
Set
<ITestResult> resultSet = tests.getResults(method);
long end = Long.MIN_VALUE;
long start = Long.MAX_VALUE;
for (ITestResult testResult : tests.getResults(method)) {
if (testResult.getEndMillis() > end) {
end
= testResult.getEndMillis();
}
if (testResult.getStartMillis() < start) {
start
= testResult.getStartMillis();
}
}
mq
+= 1;
if (mq > 1) {
buff.append(
"<tr class=\"" + style + (cq % 2 == 0 ? "odd" : "even") + "\">");
}
String description
= method.getDescription();
String testInstanceName
= resultSet.toArray(new ITestResult[] {})[0].getTestName();
// Calculate each test run times, the result shown in the html
// report.
ITestResult[] results = resultSet.toArray(new ITestResult[] {});
String methodName
= method.getMethodName();
if (setMethods.contains(methodName)) {
methods.put(methodName, methods.get(methodName)
+ 1);
}
else {
setMethods.add(methodName);
methods.put(methodName,
0);
}
String parameterString
= "";
int count = 0;

ITestResult result
= null;
if (results.length > methods.get(methodName)) {
result
= results[methods.get(methodName)];
int testId = getId(result);

for (Integer id : allRunTestIds) {
if (id.intValue() == testId)
count
++;
}
Object[] parameters
= result.getParameters();

boolean hasParameters = parameters != null && parameters.length > 0;
if (hasParameters) {
for (Object p : parameters) {
parameterString
= parameterString + Utils.escapeHtml(p.toString()) + " ";
}
}
}

int methodId = method.getTestClass().getName().hashCode();
methodId
= methodId + method.getMethodName().hashCode();
if (result != null)
methodId
= methodId + (result.getParameters() != null ? Arrays.hashCode(result.getParameters()) : 0);

buff.append(
"<td><a href=\"#m" + methodId + "\">" + qualifiedName(method) + " " + (description != null && description.length() > 0 ? "(\"" + description + "\")" : "") + "</a>" + (null == testInstanceName ? "" : "<br>(" + testInstanceName + ")") + "</td><td>" + this.getAuthors(className, method) + "</td><td class=\"numi\">" + resultSet.size() + "</td>" + "<td>" + (count == 0 ? "" : count) + "</td>" + "<td>" + parameterString + "</td>" + "<td>" + start + "</td>" + "<td class=\"numi\">" + (end - start) + "</td>" + "</tr>");
}
if (mq > 0) {
cq
+= 1;
m_out.print(
"<tr class=\"" + style + (cq % 2 == 0 ? "even" : "odd") + "\">" + "<td");
if (mq > 1) {
m_out.print(
" rowspan=\"" + mq + "\"");
}
m_out.println(
">" + lastClassName + "</td>" + buff);
}
}
}

/** Starts and defines columns result summary table */
private void startResultSummaryTable(String style) {
tableStart(style,
"summary");
m_out.println(
"<tr><th>Class</th><th>Method</th><th>Authors</th><th># of<br/>Scenarios</th><th>Running Counts</th>" + "<th>Parameters</th><th>Start</th><th>Time<br/>(ms)</th></tr>");
m_row
= 0;
}

private String qualifiedName(ITestNGMethod method) {
StringBuilder addon
= new StringBuilder();
String[] groups
= method.getGroups();
int length = groups.length;
if (length > 0 && !"basic".equalsIgnoreCase(groups[0])) {
addon.append(
"(");
for (int i = 0; i < length; i++) {
if (i > 0) {
addon.append(
", ");
}
addon.append(groups[i]);
}
addon.append(
")");
}

return "<b>" + method.getMethodName() + "</b> " + addon;
}

private void resultDetail(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
ITestNGMethod method
= result.getMethod();

int methodId = getId(result);

String cname
= method.getTestClass().getName();
m_out.println(
"<h2 id=\"m" + methodId + "\" name=\"m" + methodId + "\" >" + cname + ":" + method.getMethodName() + "</h2>");
Set
<ITestResult> resultSet = tests.getResults(method);
generateForResult(result, method, resultSet.size());
m_out.println(
"<p class=\"totop\"><a href=\"#summary\">back to summary</a></p>");

}
}

private void generateForResult(ITestResult ans, ITestNGMethod method, int resultSetSize) {
Object[] parameters
= ans.getParameters();
boolean hasParameters = parameters != null && parameters.length > 0;
if (hasParameters) {
tableStart(
"result", null);
m_out.print(
"<tr class=\"param\">");
for (int x = 1; x <= parameters.length; x++) {
m_out.print(
"<th>Parameter #" + x + "</th>");
}
m_out.println(
"</tr>");
m_out.print(
"<tr class=\"param stripe\">");
for (Object p : parameters) {
m_out.println(
"<td>" + Utils.escapeHtml(p.toString()) + "</td>");
}
m_out.println(
"</tr>");
}
List
<String> msgs = Reporter.getOutput(ans);
boolean hasReporterOutput = msgs.size() > 0;
Throwable exception
= ans.getThrowable();
boolean hasThrowable = exception != null;
if (hasReporterOutput || hasThrowable) {
if (hasParameters) {
m_out.print(
"<tr><td");
if (parameters.length > 1) {
m_out.print(
" colspan=\"" + parameters.length + "\"");
}
m_out.println(
">");
}
else {
m_out.println(
"<div>");
}
if (hasReporterOutput) {
if (hasThrowable) {
m_out.println(
"<h3>Test Messages</h3>");
}
for (String line : msgs) {
m_out.println(line
+ "<br/>");
}
}
if (hasThrowable) {
boolean wantsMinimalOutput = ans.getStatus() == ITestResult.SUCCESS;
if (hasReporterOutput) {
m_out.println(
"<h3>" + (wantsMinimalOutput ? "Expected Exception" : "Failure") + "</h3>");
}
generateExceptionReport(exception, method);
}
if (hasParameters) {
m_out.println(
"</td></tr>");
}
else {
m_out.println(
"</div>");
}
}
if (hasParameters) {
m_out.println(
"</table>");
}
}

protected void generateExceptionReport(Throwable exception, ITestNGMethod method) {
m_out.print(
"<div class=\"stacktrace\">");
m_out.print(Utils.stackTrace(exception,
true)[0]);
m_out.println(
"</div>");
}

/**
* Since the methods will be sorted chronologically, we want to return the
* ITestNGMethod from the invoked methods.
*/
private Collection<ITestNGMethod> getMethodSet(IResultMap tests, ISuite suite) {
List
<IInvokedMethod> r = Lists.newArrayList();
List
<IInvokedMethod> invokedMethods = suite.getAllInvokedMethods();

// Eliminate the repeat retry methods
for (IInvokedMethod im : invokedMethods) {
if (tests.getAllMethods().contains(im.getTestMethod())) {
int testId = getId(im.getTestResult());
if (!testIds.contains(testId)) {
testIds.add(testId);
r.add(im);
}
}
}
Arrays.sort(r.toArray(
new IInvokedMethod[r.size()]), new TestSorter());
List
<ITestNGMethod> result = Lists.newArrayList();

// Add all the invoked methods
for (IInvokedMethod m : r) {
result.add(m.getTestMethod());
}

// Add all the methods that weren't invoked (e.g. skipped) that we
// haven't added yet
// for (ITestNGMethod m : tests.getAllMethods()) {
// if (!result.contains(m)) {
// result.add(m);
// }
// }

for (ITestResult allResult : tests.getAllResults()) {
int testId = getId(allResult);
if (!testIds.contains(testId)) {
result.add(allResult.getMethod());
}
}

return result;
}

public void generateSuiteSummaryReport(List<ISuite> suites) {
tableStart(
"testOverview", null);
m_out.print(
"<tr>");
tableColumnStart(
"Test");
tableColumnStart(
"Methods<br/>Passed");
tableColumnStart(
"Scenarios<br/>Passed");
tableColumnStart(
"# skipped");
tableColumnStart(
"# failed");
tableColumnStart(
"Total<br/>Time");
tableColumnStart(
"Included<br/>Groups");
tableColumnStart(
"Excluded<br/>Groups");
m_out.println(
"</tr>");
NumberFormat formatter
= new DecimalFormat("#,##0.0");
int qty_tests = 0;
int qty_pass_m = 0;
int qty_pass_s = 0;
int qty_skip = 0;
int qty_fail = 0;
long time_start = Long.MAX_VALUE;
long time_end = Long.MIN_VALUE;
m_testIndex
= 1;
for (ISuite suite : suites) {
if (suites.size() > 1) {
titleRow(suite.getName(),
8);
}
Map
<String, ISuiteResult> tests = suite.getResults();
for (ISuiteResult r : tests.values()) {
qty_tests
+= 1;
ITestContext overview
= r.getTestContext();
startSummaryRow(overview.getName());

getAllTestIds(overview, suite);
int q = getMethodSet(overview.getPassedTests(), suite).size();
qty_pass_m
+= q;
summaryCell(q, Integer.MAX_VALUE);
q
= overview.getPassedTests().size();
qty_pass_s
+= q;
summaryCell(q, Integer.MAX_VALUE);

q
= getMethodSet(overview.getSkippedTests(), suite).size();
qty_skip
+= q;
summaryCell(q,
0);

q
= getMethodSet(overview.getFailedTests(), suite).size();
qty_fail
+= q;
summaryCell(q,
0);

time_start
= Math.min(overview.getStartDate().getTime(), time_start);
time_end
= Math.max(overview.getEndDate().getTime(), time_end);
summaryCell(formatter.format((overview.getEndDate().getTime()
- overview.getStartDate().getTime()) / 1000.) + " seconds", true);
summaryCell(overview.getIncludedGroups());
summaryCell(overview.getExcludedGroups());
m_out.println(
"</tr>");
m_testIndex
++;
}
}
if (qty_tests > 1) {
m_out.println(
"<tr class=\"total\"><td>Total</td>");
summaryCell(qty_pass_m, Integer.MAX_VALUE);
summaryCell(qty_pass_s, Integer.MAX_VALUE);
summaryCell(qty_skip,
0);
summaryCell(qty_fail,
0);
summaryCell(formatter.format((time_end
- time_start) / 1000.) + " seconds", true);
m_out.println(
"<td colspan=\"2\">&nbsp;</td></tr>");
}
m_out.println(
"</table>");
}

private void summaryCell(String[] val) {
StringBuffer b
= new StringBuffer();
for (String v : val) {
b.append(v
+ " ");
}
summaryCell(b.toString(),
true);
}

private void summaryCell(String v, boolean isgood) {
m_out.print(
"<td class=\"numi" + (isgood ? "" : "_attn") + "\">" + v + "</td>");
}

private void startSummaryRow(String label) {
m_row
+= 1;
m_out.print(
"<tr" + (m_row % 2 == 0 ? " class=\"stripe\"" : "") + "><td style=\"text-align:left;padding-right:2em\"><a href=\"#t" + m_testIndex + "\">" + label + "</a>" + "</td>");
}

private void summaryCell(int v, int maxexpected) {
summaryCell(String.valueOf(v), v
<= maxexpected);
}

private void tableStart(String cssclass, String id) {
m_out.println(
"<table cellspacing=\"0\" cellpadding=\"0\"" + (cssclass != null ? " class=\"" + cssclass + "\"" : " style=\"padding-bottom:2em\"") + (id != null ? " id=\"" + id + "\"" : "") + ">");
m_row
= 0;
}

private void tableColumnStart(String label) {
m_out.print(
"<th>" + label + "</th>");
}

private void titleRow(String label, int cq) {
titleRow(label, cq,
null);
}

private void titleRow(String label, int cq, String id) {
m_out.print(
"<tr");
if (id != null) {
m_out.print(
" id=\"" + id + "\"");
}
m_out.println(
"><th colspan=\"" + cq + "\">" + label + "</th></tr>");
m_row
= 0;
}

/** Starts HTML stream */
protected void startHtml(PrintWriter out) {
out.println(
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
out.println(
"<html xmlns=\"http://www.w3.org/1999/xhtml\">");
out.println(
"<head>");
out.println(
"<title>TestNG Report</title>");
out.println(
"<style type=\"text/css\">");
out.println(
"table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}");
out.println(
"td,th {border:1px solid #009;padding:.25em .5em}");
out.println(
".result th {vertical-align:bottom}");
out.println(
".param th {padding-left:1em;padding-right:1em}");
out.println(
".param td {padding-left:.5em;padding-right:2em}");
out.println(
".stripe td,.stripe th {background-color: #E6EBF9}");
out.println(
".numi,.numi_attn {text-align:right}");
out.println(
".total td {font-weight:bold}");
out.println(
".passedodd td {background-color: #0A0}");
out.println(
".passedeven td {background-color: #3F3}");
out.println(
".skippedodd td {background-color: #CCC}");
out.println(
".skippedodd td {background-color: #DDD}");
out.println(
".failedodd td,.numi_attn {background-color: #F33}");
out.println(
".failedeven td,.stripe .numi_attn {background-color: #D00}");
out.println(
".stacktrace {white-space:pre;font-family:monospace}");
out.println(
".totop {font-size:85%;text-align:center;border-bottom:2px solid #000}");
out.println(
"</style>");
out.println(
"</head>");
out.println(
"<body>");
}

/** Finishes HTML stream */
protected void endHtml(PrintWriter out) {
out.println(
"</body></html>");
}

// ~ Inner Classes --------------------------------------------------------
/** Arranges methods by classname and method name */
private class TestSorter implements Comparator<IInvokedMethod> {
// ~ Methods
// -------------------------------------------------------------

/** Arranges methods by classname and method name */
public int compare(IInvokedMethod o1, IInvokedMethod o2) {
// System.out.println("Comparing " + o1.getMethodName() + " " +
// o1.getDate()
// + " and " + o2.getMethodName() + " " + o2.getDate());
return (int) (o1.getDate() - o2.getDate());
// int r = ((T) o1).getTestClass().getName().compareTo(((T)
// o2).getTestClass().getName());
// if (r == 0) {
// r = ((T) o1).getMethodName().compareTo(((T) o2).getMethodName());
// }
// return r;
}
}

// ~ JavaDoc-specific Methods
// --------------------------------------------------------
/**
* Get ITestNGMethod author(s) string, or class author(s) if no method
* author is present. Default return value is "unknown".
*
*
@param className
*
@param method
*
@return
*/
private String getAuthors(String className, ITestNGMethod method) {
JavaClass cls
= builder.getClassByName(className);
DocletTag[] authors
= cls.getTagsByName("author");
// get class authors as default author name
String allAuthors = "";
if (authors.length == 0) {
allAuthors
= "unknown";
}
else {
for (DocletTag author : authors) {
allAuthors
+= author.getValue() + " ";
}
}
// get method author name
JavaMethod[] mtds = cls.getMethods();
for (JavaMethod mtd : mtds) {
if (mtd.getName().equals(method.getMethodName())) {
authors
= mtd.getTagsByName("author");
if (authors.length != 0) {
allAuthors
= "";
for (DocletTag author : authors) {
allAuthors
+= author.getValue() + " ";
}
}
break;
}
}
return allAuthors.trim();
}

/**
* Get comment string of Java class.
*
*
@param className
*
@return
*/
@SuppressWarnings(
"unused")
private String getClassComment(String className) {
JavaClass cls
= builder.getClassByName(className);
return cls.getComment();
}

/**
* Get ITestResult id by class + method + parameters hash code.
*
*
@param result
*
@return
*/
private int getId(ITestResult result) {
int id = result.getTestClass().getName().hashCode();
id
= id + result.getMethod().getMethodName().hashCode();
id
= id + (result.getParameters() != null ? Arrays.hashCode(result.getParameters()) : 0);
return id;
}

/**
* Get All tests id by class + method + parameters hash code.
*
*
@param context
*
@param suite
*/
private void getAllTestIds(ITestContext context, ISuite suite) {
IResultMap passTests
= context.getPassedTests();
IResultMap failTests
= context.getFailedTests();
List
<IInvokedMethod> invokedMethods = suite.getAllInvokedMethods();
for (IInvokedMethod im : invokedMethods) {
if (passTests.getAllMethods().contains(im.getTestMethod()) || failTests.getAllMethods().contains(im.getTestMethod())) {
int testId = getId(im.getTestResult());
// m_out.println("ALLtestid=" + testId);
allRunTestIds.add(testId);
}
}
}
}
View Code

testng.xml中加入以下代码:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
        <dependency>
<groupId>com.thoughtworks.qdox</groupId>
<artifactId>qdox</artifactId>
<version>1.12.1</version>
<scope>compile</scope>
</dependency>
View Code

5.6 用例的开头和结尾设计

BaseParpare.java:代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.base;
/**
* @Description 测试开始 和 测试结束 的操作
*
*
*/
import java.io.IOException;
import java.util.Iterator;
import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.ITestContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import com.demo.test.utils.LogConfiguration;
import com.demo.test.utils.SeleniumUtil;
public class BaseParpare {
//输出本页面日志 初始化
static Logger logger = Logger.getLogger(BaseParpare.class.getName());
protected SeleniumUtil seleniumUtil = null;
// 添加成员变量来获取beforeClass传入的context参数
protected ITestContext testContext = null;
protected String webUrl="";
protected int timeOut = 0;

@BeforeClass
/**启动浏览器并打开测试页面*/
public void startTest(ITestContext context) {
LogConfiguration.initLog(
this.getClass().getSimpleName());
seleniumUtil
= new SeleniumUtil();
// 这里得到了context值
this.testContext = context;
//从testng.xml文件中获取浏览器的属性值
String browserName = context.getCurrentXmlTest().getParameter("browserName");
timeOut
= Integer.valueOf(context.getCurrentXmlTest().getParameter("timeOut"));
webUrl
= context.getCurrentXmlTest().getParameter("testurl");


try {
//启动浏览器launchBrowser方法可以自己看看,主要是打开浏览器,输入测试地址,并最大化窗口
seleniumUtil.launchBrowser(browserName, context,webUrl,timeOut);
}
catch (Exception e) {
logger.error(
"浏览器不能正常工作,请检查是不是被手动关闭或者其他原因",e);
}
//设置一个testng上下文属性,将driver存起来,之后可以使用context随时取到,主要是提供arrow 获取driver对象使用的,因为arrow截图方法需要一个driver对象
testContext.setAttribute("SELENIUM_DRIVER", seleniumUtil.driver);
}

@AfterClass
/**结束测试关闭浏览器*/
public void endTest() {
if (seleniumUtil.driver != null) {
//退出浏览器
seleniumUtil.quit();
}
else {
logger.error(
"浏览器driver没有获得对象,退出操作失败");
Assert.fail(
"浏览器driver没有获得对象,退出操作失败");
}
}
}
View Code

在testng.xml添加如下代码:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
    <!-- chrome,firefox,ghost和ie不区分大小写 -->
<parameter name="browserName" value="chrome" />
<!-- 页面元素10秒不出现超时时间 -->
<parameter name="timeOut" value="20" />
<!-- 定义测试的站点 -->
<parameter name="testurl" value="http://127.0.0.1:1080/WebTours/" />
View Code

5.7 页面类设计

FramePage.java:代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.pages;

import org.openqa.selenium.By;

/**
* @description 这个类算不上一个page页,因为这个WebTours站点涉及到的frame比较多,所以我们把frame抓取出来用page页来存储
*
*/
public class FramePage {

/**header frame名字 */
public static final By FP_FRAME_HEADER = By.name("header");
/** body frame 名字 */
public static final By FP_FRAME_BODY = By.name("body");
/**navbar frame名字*/
public static final By FP_FRAME_NAVBAR = By.name("navbar");
/**info frame名字*/
public static final By FP_FRAME_INFO = By.name("info");
}
View Code

HomePage.java:代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.pages;

import org.openqa.selenium.By;

/**
* @description 首页面元素定位声明
*
*/
public class HomePage {

/**用户名显示区域*/
public static final By HP_TEXT_USERNAME= By.xpath("//blockquote/b");
/**Flights按钮*/
public static final By HP_BUTTON_FLIGHTS = By.xpath("//*[@src='/WebTours/images/flights.gif']");
/**Itinerary按钮*/
public static final By HP_BUTTON_ITINERARY = By.xpath("//*[@src='/WebTours/images/itinerary.gif']");
/**Home按钮*/
public static final By HP_BUTTON_HOME = By.xpath("//*[@src='/WebTours/images/in_home.gif']");
/**Sign Off按钮*/
public static final By HP_BUTTON_SIGNOFF = By.xpath("//*[@src='/WebTours/images/signoff.gif']");
/**首页完整文本*/
public static final By HP_TEXT_HOME= By.xpath("//blockquote");
}
View Code

LoginPage.java:代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.pages;

import org.openqa.selenium.By;
/**
* @description 登录页面元素定位声明
*
*/
public class LoginPage {
/**用户名输入框*/
public static final By LP_INPUT_USERNAME = By.name("username");

/**密码输入框*/
public static final By LP_INPUT_PASSWORD = By.name("password");

/**登录按钮*/
public static final By LP_BUTTON_LOGIN = By.name("login");

/**登录错误信息*/
public static final By LP_TEXT_ERROR= By.xpath("//*[@color='red']");

}
View Code

5.8 页面帮助类设计

FramePageHelper.java:代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.pageshelper;

import org.openqa.selenium.By;

import com.demo.test.utils.SeleniumUtil;

/**
* @description 这个帮助类主要是进行frame的跳进和跳出的操作
*
*/
public class FramePageHelper {

/** 进入frame-根据frame的元素定位进入 */
public static void jumpInToFrame(SeleniumUtil seleniumUtil, By by) {
seleniumUtil.switchFrame(seleniumUtil.findElementBy(by));
}

/** 回到默认的frame */
public static void jumpOut(SeleniumUtil seleniumUtil) {
seleniumUtil.outFrame();
}
}
View Code

HomePageHelper.java:代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.pageshelper;

import org.apache.log4j.Logger;

import com.demo.test.pages.FramePage;
import com.demo.test.pages.HomePage;
import com.demo.test.utils.SeleniumUtil;

/**
* @desciption 首页帮助类:专门提供在这个页面进行操作的方法封装
*/
public class HomePageHelper {
// 提供本类中日志输出对象
public static Logger logger = Logger.getLogger(HomePageHelper.class);

/**
*
@author Young
* @description 等待首页元素加载
*
@param seleniumUtil
* selenium api封装引用对象
*
@param timeOut
* 等待元素超时的时间
*
*/
public static void waitHomePageLoad(SeleniumUtil seleniumUtil, int timeOut) {
FramePageHelper.jumpOut(seleniumUtil);
// 等待body frame显示出来
seleniumUtil.waitForElementToLoad(timeOut, FramePage.FP_FRAME_BODY);
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);
// 先进入到body
// frame中
// 等待navbar frame显示出来
seleniumUtil.waitForElementToLoad(timeOut, FramePage.FP_FRAME_NAVBAR);
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_NAVBAR);
// 再进入body
// frame的子frame:navbar
// frame中
logger.info("开始等待首页元素加载");
seleniumUtil.waitForElementToLoad(timeOut, HomePage.HP_BUTTON_FLIGHTS);
seleniumUtil
.waitForElementToLoad(timeOut, HomePage.HP_BUTTON_ITINERARY);
seleniumUtil.waitForElementToLoad(timeOut, HomePage.HP_BUTTON_HOME);
seleniumUtil.waitForElementToLoad(timeOut, HomePage.HP_BUTTON_SIGNOFF);
logger.info(
"首页元素加载完毕");
FramePageHelper.jumpOut(seleniumUtil);

}

/**
* @Description 登录成功之后验证用户名是不是正确的
*
*/
public static void checkUserName(SeleniumUtil seleniumUtil, int timeOut,
String username) {
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);
// 先进入到body
// frame中
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_INFO);// 先进入到body
// frame中的子frame:info
// frame中
logger.info("开始检查用户名是不是:" + username);
seleniumUtil.isTextCorrect(
seleniumUtil.getText(HomePage.HP_TEXT_USERNAME), username);
logger.info(
"用户名检查完毕,用户名是:" + username);

}

/**
* @description 登录成功之后验证首页的文本内容
*
*/
public static void checkHomeText(SeleniumUtil seleniumUtil) {
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);
// 先进入到body
// frame中
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_INFO);// 先进入到body
// frame中的子frame:info
// frame中
seleniumUtil
.isTextCorrect(
seleniumUtil.getText(HomePage.HP_TEXT_HOME),
"Welcome, jojo, to the Web Tours reservation pages."
+ "\n"
+ "Using the menu to the left, you can search for new flights to book, or review/edit the flights already booked. Don't forget to sign off when you're done!");

}

}
View Code

LoginPageHelper.java:代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.pageshelper;

import org.apache.log4j.Logger;

import com.demo.test.pages.FramePage;
import com.demo.test.pages.LoginPage;
import com.demo.test.utils.SeleniumUtil;

/**
* @description 登录页面帮助类:提供在这个页面上做的操作的方法封装
*
*/
public class LoginPageHelper {
// 提供本类中日志输出对象
public static Logger logger = Logger.getLogger(LoginPageHelper.class);

/**
* @description 等待登录页面元素加载
*
@param seleniumUtil
* selenium api封装引用对象
*
@param timeOut
* 等待元素超时的时间
*
*/
public static void waitLoginPageLoad(SeleniumUtil seleniumUtil, int timeOut) {
seleniumUtil.pause(
1000);
// 对此处的解释:这个登录页面有两个大frame,一个header一个body ,
// 而登录的用户名、密码输入框以及登录按钮都在body frame下的navbar frame下,
// 所以先要进入body frame中,然后在进入navbar frame中,才能查找到登录界面的相关元素
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);// 先进入到body
// frame中
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_NAVBAR);// 再进入body
// frame的子frame:navbar
// frame中
logger.info("开始检查登录页面元素");
seleniumUtil.waitForElementToLoad(timeOut, LoginPage.LP_INPUT_USERNAME);
seleniumUtil.waitForElementToLoad(timeOut, LoginPage.LP_INPUT_PASSWORD);
seleniumUtil.waitForElementToLoad(timeOut, LoginPage.LP_BUTTON_LOGIN);
logger.info(
"检查登录页面元素完毕");
}

/**
* @description 登录操作封装
*
@param seleniumUtil
* selenium api封装引用对象
*
@param username
* 用户名值
*
@param password
* 用户密码值
*
*/
public static void typeLoginInfo(SeleniumUtil seleniumUtil,
String username, String password) {

logger.info(
"开始输入登录信息");
// 清空用户名输入框
seleniumUtil.clear(LoginPage.LP_INPUT_USERNAME);
// 输入用户名到用户名输入框
seleniumUtil.type(LoginPage.LP_INPUT_USERNAME, username);
// 清空密码输入框
seleniumUtil.clear(LoginPage.LP_INPUT_PASSWORD);
// 输入密码到密码输入框
seleniumUtil.type(LoginPage.LP_INPUT_PASSWORD, password);
logger.info(
"输入登录信息完毕");
// 点击登录按钮
seleniumUtil.click(LoginPage.LP_BUTTON_LOGIN);

}

/**
* @description 验证登录错误信息
*
@param seleniumUtil
* selenium api封装引用对象
*
@param error
* 错误文本
*
*/
public static void checkLoginErrorInfo(SeleniumUtil seleniumUtil,
String error) {
FramePageHelper.jumpOut(seleniumUtil);
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);
FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_INFO);
seleniumUtil.isTextCorrect(
seleniumUtil.getText(LoginPage.LP_TEXT_ERROR), error);
}
}
View Code

5.9 书写第一个用例

LoginPage_001_LoginSuccessFunction_Test:第一部分:LoginPage表明你是哪个模块的;第二部分:001,分用例编号,这个晚点会讲到用例编号的作用;第三部分:LoginSuccessFuntion这个是你用例具体要干嘛的一个简称;第四部分:Test每个用例以这个作为结尾后缀。每个部分之间必须以下划线”_”作为分隔符 。代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
package com.demo.test.testcases.login;
import java.util.Map;
import org.testng.annotations.Test;
import com.demo.test.base.BaseParpare;
import com.demo.test.pageshelper.HomePageHelper;
import com.demo.test.pageshelper.LoginPageHelper;
/**
* @description 登录之后验证用户名是不是正确的
*
*/

public class LoginPage_001_LoginSuccessFunction_Test extends BaseParpare{
@Test
public void loginSuccessFunction() {
//等待登录页面加载
LoginPageHelper.waitLoginPageLoad(seleniumUtil, timeOut);
// 输入登录信息
LoginPageHelper.typeLoginInfo(seleniumUtil,"jojo", "bean");
//等待首页元素显示出来
HomePageHelper.waitHomePageLoad(seleniumUtil, timeOut);
//检查用户名是不是期望的"jojo"
HomePageHelper.checkUserName(seleniumUtil, timeOut, "jojo");
}
}
View Code

在testng.xml中添加代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
    <!-- 定义测试模块,用test标签包围 -->    
<test name="Login" preserve-order="true">
<packages>
<package name="com.demo.test.testcases.login" />
</packages>
</test>
View Code

5.10 完整的pom.xml和testng.xml

pom.xml代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>autotest</artifactId>
<version>0.0.1-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>com.codeborne</groupId>
<artifactId>phantomjsdriver</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.qdox</groupId>
<artifactId>qdox</artifactId>
<version>1.12.1</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
<version>LATEST</version>
</dependency>
</dependencies>

<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
View Code

testng.xml代码如下:

Selenium Web 自动化 - 项目实战(一)Selenium Web 自动化 - 项目实战(一)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
<!-- driver的属性配置文件保存路径 -->
<parameter name="driverConfgFilePath" value="config/driver.properties" />
<!-- chrome,firefox,ghost和ie不区分大小写 -->
<parameter name="browserName" value="chrome" />
<!-- 页面元素10秒不出现超时时间 -->
<parameter name="timeOut" value="20" />
<!-- 定义测试的站点 -->
<parameter name="testurl" value="http://127.0.0.1:1080/WebTours/" />

<!-- 定义测试模块,用test标签包围 -->
<test name="Login" preserve-order="true">
<packages>
<package name="com.demo.test.testcases.login" />
</packages>
</test>

<listeners>
<!-- arrow插件实现用例失败重跑以及生成测试报告 -->
<listener class-name="com.demo.test.plugins.arrow.TestResultListener" />
<listener class-name="com.demo.test.plugins.arrow.RetryListener" />
<listener class-name="com.demo.test.plugins.arrow.PowerEmailableReporter" />
</listeners>
</suite> <!-- Suite -->
View Code


 

6 配置测试报告目录


 返回

Window->Preferences,然后再找到testng选项:Output directory,默认路径是/test-output 也就是项目根目录下会生成一个test-output的目录。修改为:/result/test-report

7 填加driver


 返回

根据需要添加不同浏览器的driver。

8 执行用例


 返回

右击testng.xml -> runas -> TestNG Suite