Selenium Web 自动化框架

来源:互联网 发布:乐多捕鱼源码 编辑:程序博客网 时间:2024/05/21 17:35

 

目录

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,创建如下结构

图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,

图2 Creat new POM

生成pom.xml如下所示:

复制代码
<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>
复制代码

其中:

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

3 加入TestNG配置文件 


 返回

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

<?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 -->

4 Eclipse编码修改


 返回

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

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

5 编写代码


 返回

名称描述链接 这个类的主要作用是启动和关闭浏览器 5.6

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

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

5.1 封装Selenium操作

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

复制代码
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);            }        }    }}
复制代码

pom.xml中添加代码如下:

复制代码
        <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>
复制代码

5.2 使用log4j进行日志输出

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

复制代码
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;            }    }
复制代码

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

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

复制代码
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;    }}
复制代码

pom.xml中添加代码如下:

        <dependency>            <groupId>com.codeborne</groupId>            <artifactId>phantomjsdriver</artifactId>            <version>1.2.1</version>        </dependency>

testng.xml中添加代码如下:

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

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

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

复制代码
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));    }}
复制代码

driver.properties代码如下:

复制代码
# define the browser driver here #chrome driver for windowschromedriver_win =res/driver/chrome/win/chromedriver.exe#chrome driver for linuxchomedriver_linux =res/driver/chrome/linux/chromedriver#chrome driver for mac oschomedriver_mac = res/driver/chrome/mac/chromedriver#chrome driver for IE iedriver = res/driver/ie/iedriver.exe#ghost driver for windowsghostdriver_win =res/driver/ghostdriver/phantomjs.exe
复制代码

5.5 arrow插件解析

5.5.1 ConfigReader.java

代码如下:

复制代码
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;    }}
复制代码

config.properties代码如下:

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

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

TestResultListener.java代码如下:

复制代码
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 + "\"/>");        }    }}
复制代码

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

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

复制代码
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);        }    }}
复制代码

TestngRetry.java代码如下:

复制代码
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;    }}
复制代码

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

PowerEmailableReporter.java:代码如下:

复制代码
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);            }        }    }}
复制代码

testng.xml中加入以下代码:

        <dependency>            <groupId>com.thoughtworks.qdox</groupId>            <artifactId>qdox</artifactId>            <version>1.12.1</version>            <scope>compile</scope>        </dependency>

5.6 用例的开头和结尾设计

BaseParpare.java:代码如下:

复制代码
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没有获得对象,退出操作失败");        }    }}
复制代码

在testng.xml添加如下代码:

    <!-- 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/" />

5.7 页面类设计

FramePage.java:代码如下:

复制代码
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");}
复制代码

HomePage.java:代码如下:

复制代码
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");    }
复制代码

LoginPage.java:代码如下:

复制代码
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']");    }
复制代码

5.8 页面帮助类设计

FramePageHelper.java:代码如下:

复制代码
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();    }}
复制代码

HomePageHelper.java:代码如下:

复制代码
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!");    }}
复制代码

LoginPageHelper.java:代码如下:

复制代码
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);    }}
复制代码

5.9 书写第一个用例

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

复制代码
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");  }}
复制代码

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

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

5.10 完整的pom.xml和testng.xml

pom.xml代码如下:

复制代码
<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>
复制代码

testng.xml代码如下:

复制代码
<?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 -->
复制代码


 

6 配置测试报告目录


 返回

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

7 填加driver


 返回

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

8 执行用例


 返回

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

 

原创粉丝点击