本系列所有代码 https://github.com/zhangting85/simpleWebtest

本文将介绍一个Java+TestNG+Maven+Selenium的web自动化测试脚本环境下selenium页面对象脚本的编写,并提供全部代码。

文中将看到,使用selenium编写的一个高复用性、高可维护性的测试框架的核心部分。

背景就不多介绍了,不用页面对象设计的selenium测试框架多半会存在巨大缺陷,长时间使用后问题多多,并且导致了N多项目失败。或者即使不失败,也让自动化测试人员感觉非常厌烦。比如我见过的一些失败框架:有用一个类封装了一百二十几个selenium操作,用了一个三千多行的上帝类的;有完全不能重用全部用线性代码实现,开发人员小小改动一行代码,自动化测试人员改30多个文件的;还有试图用Spring去实现testNG的测试配置、执行功能的;有不管什么东西都先放个接口再implements的;总之奇奇怪怪的设计千万种(上帝类特别多,上帝类其实真的很累,给上帝减负吧亲。)。

以下是我的精简设计,仅供参考

3个核心类:Page、TestCase、DriverManager; 由于太简单,类图就略了。

Page是所有页面对象的父亲,并在构造方法里调用selenium页面工厂初始化页面。

TestCase是所有测试用例的父亲,一方面,他定义了每个测试用例的前置操作和后置操作,另一方面,他内部有一个嵌套类DriverManager负责处理driver。

DriverManager是TestCase的静态内部类,负责处理driver相关的操作

由于我的测试用例是由TestNG来执行的,通常从一个xml文件开始。指定执行某个包下所有带@Test标签的方法(即测试方法)。这个机制使得我们不用亲自去创建TestCase类的实例。(有的开发人员用Spring来创建TestCase的实例,但我用TestNG更简便。两者实现的功能都是通过配置文件,由框架创建我们需要执行的TestCase类的实例并执行他。)

先贴上代码:

Page

 public class Page {
/**
* 构造方法,被所有Page的子类继承,所以每个页面都可以通过自动调用这个方法来初始化页面对象
* it auto calls by all sub-page
*/
public Page() {
PageFactory.initElements(DriverManager.driver, this);
} }

TestCase和DriverManager

 package simplewebtest.core;

 import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.events.EventFiringWebDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
/**
* 所有TestNG TestCase都继承这个类。
* 这个类的功能是让testNG可以设置他的driver类型
* 以及由他的成员driver manager来操作这个case要用到的driver
*/
public class TestCase { /**
* 打log用的对象,this表示具体的子类。
* print log
*/
protected Log log = LogFactory.getLog(this.getClass()); /**
* 决定这个TestCase是用什么浏览器的driver来执行。
* 由于设置了BeforeMethod标签,这个方法将由TestNG在每个TestMethod被执行前调用。
* 他将接收一个从TestNG的xml文件传入的参数表示浏览器种类。
* 告诉manager我要新建的driver的类型。
* runs by testNG, it will be run before every test method to decide the driver type
* @param browser:从testng的xml文件传入的浏览器名称。 默认值为firefox
*/
@BeforeMethod(alwaysRun=true)
@Parameters("brwoser")
protected void testMethodStart(@Optional("firefox") String browser){
DriverManager.setDriver(browser);
} /**
* 在一个测试方法结束时再打一遍名字关闭driver,这部分可以根据需要调整
* runs by testNG, it will be run after every test method to close the driver
*/
@AfterMethod(alwaysRun=true)
protected void testMethodEnd(){
DriverManager.quitDriver();;
} /**
* 打印类名。建议一个CASE只放一个方法。
* Print Class Name
*/
@BeforeClass(alwaysRun=true)
protected void testCaseStart(){
//打印类名
log.info("\\/\\/\\/\\/\\/\\/---TestCase = "+ this.getClass().getSimpleName()+"---\\/\\/\\/\\/\\/\\/");
} /**
* 再次打印类名
* Print Class Name again and separator
*/
@AfterClass(alwaysRun=true)
protected void testCaseEnd(){
//打印类名
log.info("/\\/\\/\\/\\/\\/\\---TestCase = "+ this.getClass().getSimpleName()+"---/\\/\\/\\/\\/\\/\\");
//打印分隔符
log.info("#####################################################");
} /**
* 静态内部类。因为把这些driver相关的东西直接放在TestCase类里,我感觉从逻辑上说不通。引入一个静态内部类来解决。
*/
public static class DriverManager {
/**
* 每个DriverManager只管理一个driver,所以他是static的
* shares the same web driver
*/
public static WebDriver driver;
/**
* 根据TestCase的要求来新建一个driver并保存起来
* crate and saves the driver according to the browser type
*/
public static void setDriver(String browser){
if (browser.equals("firefox")){
driver = new EventFiringWebDriver(new FirefoxDriver()).register(new LogEventListener());
}
//有需求的同学自己在这里添加IE等浏览器的支持
//you can add ie/chrome or other driver here
} /**
* 关浏览器,Windows上需要在这里杀进程的步骤
* quit the driver
*/
public static void quitDriver(){
driver.quit();
} }
}

当测试执行时,原本testNG应该去创建某个具体测试用例(TestCase的某个子类),然后调用里面的@Test方法来执行测试。但是现在这些具体的测试用例有了共有的父类,Java里创建子类实例也就是调用子类构造方法时,会先调用其父类的构造方法,也就是创建他的父类实例。然后TestNG就知道了有这个实例。

接着,因为我们在他的父类也就是TestCase类里定义了使用@BeforeClass @BeforeMethod标签的两个方法,所以在执行子类的@Test前TestNG实际上会先执行这两个方法。于是,我可以在这两个方法里面去打log和启动浏览器。这里,启动浏览器的参数是由TestNG.xml中传入的。我们可以配置好告诉testNG我要用什么浏览器,也可以传入更多配置参数实现更丰富的测试组合。

之后,进入正式的测试用例执行阶段,这里给一个测试用例的例子:

TestJDSearch

package simplewebtest.test.testcases.sample.jd;

import org.testng.annotations.Test;

import simplewebtest.core.TestCase;
import simplewebtest.core.page.sample.jd.JDHomepage;
import simplewebtest.core.page.sample.jd.JDItemlistPage; public class TestJDSearch extends TestCase { /**
* JD首页上搜索一个商品 主要介绍定位某个商品名称的N种写法
*/
@Test
public void searchProduct() throws InterruptedException {
log.info("这是测试方法里的第一句打印的log");
JDHomepage home = new JDHomepage(); //结果页面the expected result page
JDItemlistPage resultPage=home.init().searchHeader.search("巧克力");
//actual result: 用三种方法找出第一个商品名字,作为实际结果.(回字有五种写法:P) String product_1= resultPage.firstproduct.getText();//不推荐,但偶尔有适用场景
String product_2= resultPage.getFirstProductName();//不推荐,但偶尔有适用场景
String product_3= resultPage.getProductNameByIndexMethodOne(1);//推荐写法,但你方法名字不要这么长
String product_4= resultPage.getProductNameByIndexMethodTwo(1);//推荐写法,但你方法名字不要这么长 log.info("第一个商品名字(用第一种取法)= "+product_1);
log.info("第一个商品名字(用第二种取法)= "+product_2);
log.info("第一个商品名字(用第三种取法)= "+product_3);
log.info("第一个商品名字(用第四种取法)= "+product_4); //不加asseriton你的case永远是pass
assert(product_1.contains("巧克力"));
assert(product_1.equals(product_2));
assert(product_1.equals(product_3));
assert(product_1.equals(product_4)); log.info("这是测试方法里的最后一句打印的log");
} }

(这里用到的测试页面JDHomepage我会在下一期详解,但代码在我的github工程上已经有了)

这里,首先新建了一个JDHomepage,他是Page类的子类,并且我没有为他定义构造函数。所以他这里new一个对象时调用的其实是父类Page的构造函数。在这个构造函数里,page类调用了selenium的PageFactory来初始化页面。(所以请开发转的自动化测试人员们不要再通过集成Spring来创建页面了。。)

不管是在具体case里还是在具体page里,我都可以直接使用DriverManager.driver来访问当前driver,因为这个类和对象是静态的并且在test case里BeforeMethod里已经进行初始化。

另外,这个静态的driver会随着test case的创造而创造,随着test case的毁灭而毁灭。也就是说对每一个具体的test case,他会重新被创建和销毁。

如果觉得不理解整个过程,建议下载源代码,单步走一遍。就知道testNG下执行这个测试框架时他的执行流程了。

下一篇继续深入页面对象,讲官网上没有的东西:页面模块。

selenium从入门到应用 - 4,页面对象设计模式的实现的更多相关文章

  1. selenium从入门到应用 - 5,页面对象设计模式下的页面模块

    本系列所有代码 https://github.com/zhangting85/simpleWebtest 本文将介绍一个Java+TestNG+Maven+Selenium的web自动化测试脚本环境下 ...

  2. [译]Selenium Python文档:六、页面对象

    本章是介绍页面对象设计模式的教程.一个页面对象代表了web应用用户接口的一片区域,你的测试代码将与之交互的. 使用页面对象模式的好处: 可以创建在多个测试样例中都可使用的可重用代码 减少重复性代码 如 ...

  3. PageObject 页面对象模式

    一.PageObject 页面对象设计模式  (一个页面建一个类,即对象,页面对象) 每个页面都建对应的class,类中包含了页面的输入框.标题.元素等元素,测试代码中测试这个页面时,只需要调用这个页 ...

  4. selenium测试框架篇,页面对象和元素对象的管理

    前期已经做好使用Jenkins做buildhttp://www.cnblogs.com/tobecrazy/p/4529399.html 做自动化框架,不可避免的就是对象库. 有一个好的对象库,可以让 ...

  5. Selenium自动化中DOM,XPATH,CSS定位Web页面对象的优劣性分析

    加速IE浏览器自动化执行效率:Selenium自动化中DOM,XPATH,CSS定位Web页面对象的优劣性分析 1.技术背景       在Web应用中,用户通过键盘在输入框中输入值和鼠标点击按钮,链 ...

  6. selenium webdriver(3)---操作页面对象

    页面对象的相关操作可以通过接口文件org.openqa.selenium.WebElement查看,本文只是对象接口的使用方式,具体的实现方式在org.openqa.selenium.remote.R ...

  7. Selenium高亮页面对象

    使用QTP习惯了,在QTP中可以通过访问对象的highlight方法直接高亮对象,确实很方便,那么如何让Selenium高亮页面的测试对象了,可以通过javascript修改页面对象的属性进而高亮对象 ...

  8. Selenium(Python)页面对象+数据驱动测试框架

    整个工程的目录结构: 常用方法类: class SeleniumMethod(object): # 封装Selenium常用方法 def __init__(self, driver): self.dr ...

  9. Selenium(Python)PageObject页面对象

    使用PageObject页面对象的好处是, 当页面元素的位置发生改变时, 只需要去修改Xpath或者ID, 而不用去修改测试用例本身: 本次的思路是: 1.常用方法类 2.页面对象类 3.测试用例类 ...

随机推荐

  1. WIN8下笔记本共享热点的设置

    C:\windows\system32>netsh wlan start hostednetwork 无法启动承载网络. 组或资源的状态不是执行请求操作的正确状态. 解决方法: http://j ...

  2. cocos2d-iphone 与 UI组件

    http://zhidao.baidu.com/link?url=v9d7y2doWqcPhKz1lz8TkO7ZQfslg-e-55JE0XP9VdKJ0vHobcLPEAXaXjD2lD-TGmg ...

  3. Windows8 上用Ubuntu-Ubuntu启动SSH

    公司刚给配了一个电脑,华硕的超级本8个G的内存,很强大的了,但是系统是64位的windows 8,我用wubi.exe直接安装到系统上,但是开机启动的时候总是下面这个错误,去Ubuntu社区请教,结论 ...

  4. Appium+python自动化7-输入中文【转载】

    前言 在做app自动化过程中会踩很多坑,咱们都是用的中文的app,所以首先要解决中文输入的问题! 本篇通过屏蔽软键盘,绕过手机的软键盘方法,解决中文输入问题. 一.定位搜索 1.打开淘宝点搜索按钮,进 ...

  5. 跨集群拷贝hdfs

    拷贝 A集群的test目录到B集群的test目录 问题1: cause:java.io.IOException: Failed to run job : User root cannot submit ...

  6. Codeforces Beta Round #25 (Div. 2 Only) A. IQ test【双标记/求给定数中唯一的奇数或偶数】

    A. IQ test time limit per test 2 seconds memory limit per test 256 megabytes input standard input ou ...

  7. flow JavaScript 静态类型检查工具

    内置类型 flow 内置类型有 boolean, number, string, null, void, any, mixed, literal type. 其中 boolean, number, s ...

  8. linux之ifconfig命令

    1.命令格式: ifconfig [网络设备] [参数] 2.命令功能: ifconfig 命令用来查看和配置网络设备.当网络环境发生改变时可通过此命令对网络进行相应的配置. 3.命令参数: up 启 ...

  9. js常用函数和常用技巧

    学习和工作的过程中总结的干货,包括常用函数.常用js技巧.常用正则表达式.git笔记等.为刚接触前端的童鞋们提供一个简单的查询的途径,也以此来缅怀我的前端学习之路. PS:此文档,我会持续更新. Aj ...

  10. Python中的XMLRPC(Calendar)

    XMLRPC服务器端代码,定义了三个方法名,getMonth获取所给月份的日历,getYear获取一年的日历,getDict测试返回字典的效果. xmlrpc_server.py: #!/usr/bi ...