selenium从入门到应用 - 4,页面对象设计模式的实现
2014-04-06 16:12
411 查看
本系列所有代码 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
TestCase和DriverManager
当测试执行时,原本testNG应该去创建某个具体测试用例(TestCase的某个子类),然后调用里面的@Test方法来执行测试。但是现在这些具体的测试用例有了共有的父类,Java里创建子类实例也就是调用子类构造方法时,会先调用其父类的构造方法,也就是创建他的父类实例。然后TestNG就知道了有这个实例。
接着,因为我们在他的父类也就是TestCase类里定义了使用@BeforeClass @BeforeMethod标签的两个方法,所以在执行子类的@Test前TestNG实际上会先执行这两个方法。于是,我可以在这两个方法里面去打log和启动浏览器。这里,启动浏览器的参数是由TestNG.xml中传入的。我们可以配置好告诉testNG我要用什么浏览器,也可以传入更多配置参数实现更丰富的测试组合。
之后,进入正式的测试用例执行阶段,这里给一个测试用例的例子:
TestJDSearch
(这里用到的测试页面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下执行这个测试框架时他的执行流程了。
下一篇继续深入页面对象,讲官网上没有的东西:页面模块。
本文将介绍一个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从入门到应用 - 5,页面对象设计模式下的页面模块
- 面向对象程序设计(OOP设计模式)-行为型模式之观察者模式的应用与实现
- Python3-Selenium3使用PO设计模式(Page Object)实现简单的页面登录操作
- 设计模式用过哪些,应用场景是什么;单例模式有几种实现方式,代码怎么写?
- iOS应用设计模式开发中职责链(责任链)模式的实现解析
- 利用C#实现游戏应用设计模式(编写周期2天)
- 编程语言入门及进阶、设计模式、面向对象书籍
- [.net 面向对象程序设计深入](31)实战设计模式——使用Ioc模式(控制反转或依赖注入)实现松散耦合设计(1)
- 【日积月累】设计模式之单例模式的解析及C++应用实现
- 设计模式observer在ajax中的一个应用,解决页面onload函数同一时间只有一个可以用
- 本文是笔者根据数据库编程经验,利用C++语言的模板、继承、授权、多态等面向对象特性,借鉴命令模式,实现了对象在关系数据中的存储,降低应用系统与数据库之间的耦合,提高开发效率。
- 代理对象我所理解的设计模式(C++实现)——代理模式(Proxy Pattern)
- JS设计模式之观察者模式实现实时改变页面中金额数的方法
- [设计模式]JavaScript钩子机制的应用场景与实现
- 解决全站乱码的过滤器实现-包装类设计模式的应用
- 设计模式必须通过面向对象来实现吗?
- android应用开发-从设计到实现 2-8 组件与常用模式
- C语言的设计模式-面向对象机制的实现(一)
- C++实现设计模式 - STATE(状态)—对象行为模式(第二十个)
- selenium实现嵌套页面对象实例--查询功能(POM)