selenium从入门到应用

摘要:
我们将看到使用selenium编写的高度可重用和可维护的测试框架的核心部分。背景介绍不多。大多数没有页面对象的硒测试框架都会有巨大的缺陷。使用时间长了,会出现很多问题,导致N多个项目失败。Page是所有页面对象的父亲,并在构造方法中调用selenium页面工厂来初始化页面。16*此类的功能是使testNG能够设置其驱动程序类型17*和其成员drivermanager用于操作此案例的driver18*/19publicclassTestCase{2021/**22*。这表示特定的子类。

本系列所有代码 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

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

TestCase和DriverManager

  1 package simplewebtest.core;
  2 
  3 import org.apache.commons.logging.Log;
  4 import org.apache.commons.logging.LogFactory;
  5 import org.openqa.selenium.WebDriver;
  6 import org.openqa.selenium.firefox.FirefoxDriver;
  7 import org.openqa.selenium.support.events.EventFiringWebDriver;
  8 import org.testng.annotations.AfterClass;
  9 import org.testng.annotations.AfterMethod;
 10 import org.testng.annotations.BeforeClass;
 11 import org.testng.annotations.BeforeMethod;
 12 import org.testng.annotations.Optional;
 13 import org.testng.annotations.Parameters;
 14 /**
 15  * 所有TestNG TestCase都继承这个类。
 16  * 这个类的功能是让testNG可以设置他的driver类型
 17  * 以及由他的成员driver manager来操作这个case要用到的driver
 18  */
 19 public class TestCase {
 20 
 21     /**
 22      * 打log用的对象,this表示具体的子类。
 23      * print log
 24      */
 25     protected Log log = LogFactory.getLog(this.getClass());
 26     
 27     /**
 28      * 决定这个TestCase是用什么浏览器的driver来执行。
 29      * 由于设置了BeforeMethod标签,这个方法将由TestNG在每个TestMethod被执行前调用。
 30      * 他将接收一个从TestNG的xml文件传入的参数表示浏览器种类。
 31      * 告诉manager我要新建的driver的类型。
 32      * runs by testNG, it will be run before every test method to decide the driver type 
 33      * @param browser:从testng的xml文件传入的浏览器名称。 默认值为firefox
 34      */
 35     @BeforeMethod(alwaysRun=true)
 36     @Parameters("brwoser")
 37     protected void testMethodStart(@Optional("firefox") String browser){
 38     DriverManager.setDriver(browser);
 39     }
 40     
 41     /**
 42      * 在一个测试方法结束时再打一遍名字关闭driver,这部分可以根据需要调整
 43      * runs by testNG, it will be run after every test method to close the driver
 44      */
 45     @AfterMethod(alwaysRun=true)
 46     protected void testMethodEnd(){
 47     DriverManager.quitDriver();;
 48     }
 49     
 50     /**
 51      * 打印类名。建议一个CASE只放一个方法。
 52      * Print Class Name
 53      */
 54     @BeforeClass(alwaysRun=true)
 55     protected void testCaseStart(){
 56     //打印类名
 57     log.info("\/\/\/\/\/\/---TestCase = "+ this.getClass().getSimpleName()+"---\/\/\/\/\/\/");
 58     }
 59     
 60     /**
 61      * 再次打印类名
 62      * Print Class Name again and separator
 63      */
 64     @AfterClass(alwaysRun=true)
 65     protected void testCaseEnd(){
 66     //打印类名
 67     log.info("/\/\/\/\/\/\---TestCase = "+ this.getClass().getSimpleName()+"---/\/\/\/\/\/\");
 68     //打印分隔符
 69     log.info("#####################################################");
 70     }
 71     
 72     /**
 73      * 静态内部类。因为把这些driver相关的东西直接放在TestCase类里,我感觉从逻辑上说不通。引入一个静态内部类来解决。
 74      */
 75     public static class DriverManager {
 76         /**
 77          * 每个DriverManager只管理一个driver,所以他是static的 
 78          * shares the same web driver
 79          */
 80         public static WebDriver driver;
 81         /**
 82          * 根据TestCase的要求来新建一个driver并保存起来
 83          * crate and saves the driver according to the browser type
 84          */
 85         public static void setDriver(String browser){
 86             if (browser.equals("firefox")){
 87                  driver = new EventFiringWebDriver(new FirefoxDriver()).register(new LogEventListener());
 88             }
 89             //有需求的同学自己在这里添加IE等浏览器的支持
 90             //you can add ie/chrome or other driver here
 91         }        
 92         
 93         /**
 94          * 关浏览器,Windows上需要在这里杀进程的步骤
 95          * quit the driver
 96          */
 97         public static void quitDriver(){
 98             driver.quit();
 99         }
100 
101     }
102 }

当测试执行时,原本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从入门到应用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇如何创建Node.js Web服务器File类中的listFile方法还可使用文件过滤器下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Selenium操作示例——鼠标悬停显示二级菜单,再点击二级菜单或下拉列表

这两天在玩python中selenium,遇到一个问题,就是鼠标移动到页面中某按钮或菜单,自动弹出二级菜单或下拉菜单,再自动点击其中的二级菜单或下拉列表。 首先,手工操作:打开母校的主页 http://www.uestc.edu.cn/,将鼠标移动到“学校概括”,自动弹出二级菜单,手工点击其中的“学校简介”,弹出学校的简介。 如何在python中使用se...

selenium显式等待的封装

显式等待不像隐式等待和强制等待一样,浪费时间,显式等待是指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常。只有该条件触发,才执行后续代码,这个使用更灵活。 显式等待是一种智能的等待,但它只能应用于指定的元素,等待动态加载的Ajax元素 源码注解: 参数解释 driver:webdriver的实例对象 timeout:最长...

第七部分(三) 动态渲染页面爬取(用Selenium获取淘宝商品,不涉及验证登录)

三、 使用 Selenium 爬取淘宝商品在分析 Ajax 抓取相关数据时,不是所有页面都可以通过分析 Ajax 来完成抓取。比如淘宝的整个页面数据确实是通过 Ajax 获取的,但这些 Ajax 接口参数复杂,并且包含有加密密钥等,如果要构造 Ajax 参数是很困难。像这种页面最方便的抓取方法是通过 Selenium 。接下就用 Selenium 模拟浏览...

Selenium常见的无法定位到元素的原因

1.元素没有加载完成,就进行定位元素 设置等待:导入时间模块使用  time.sleep(5) 2.窗口切换  窗口句柄还处在上一个窗口,导致无法定位新窗口的元素。 3.Frame原因定位不到元素 这个是最常见的原因,首先要理解下frame的实质,frame中实际上是嵌入了另一个页面,而webdriver每次只能在一个页面识别, 因此需要先定位到相应的f...

3分钟手把手带你搭建基于selenium的自动化框架

1 、什么是seleniumSelenium 是一个基于浏览器的自动化工具,它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分:Selenium IDE、Selenium WebDriver 和Selenium Grid: Selenium IDE:Firefox的一个扩展,它可以进行录制回放,并可以把录制的操作以多...

testng参数化(提供测试数据)

testng提供测试数据的两个注释:@DataProvide和@Parameter   一、通过testng.xml中设置参数 (实际上testng.xml只是一个名字,可以起任何一个名字,只要是.xml文件,然后文件格式按照testng的文档格式DTD来就可以了) testng.xml中的内容: <?xml version="1.0" encodi...