Selenium之POM模式:自定义 VS PageFactory

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ouyanggengcheng/article/details/84988533

    什么是POM(page object model)?从英文解释为页面对象模式,说的很清楚,玩过QTP的朋友就有点懵懂了;不错,就是有点对象库的意思。还记得前面说的QTP故事的时候说到对象库编程与描述性编程吗?可以回去看看。对象库就更好理解了,就是管理对象的一个仓库,而从selenium的POM模式来说,是每个页面的对象分为一个对象库;再说明白点就是每个页面的对象定位管理起来。

一、自定义POM模式

1.基类BasePage

public class BasePage {

      

       private static Logger logger = Logger.getLogger(BasePage.class);

      

       public WebDriver driver;

      

       public BasePage(){

              this.driver = WebDriverMethod.ThreadDriver.get();

              logger.info("使用BasePage类初始化driver对象");

       }

      

       /**

        * 智能,元素不可见时,最多等待10S,10S内找到元素立即返回

        * @param locator

        * @return

        */

       public WebElement AutoWait(By locator){

              WebElement webElement = null;

              try {

                     WebDriverWait wait = new WebDriverWait(driver,10);

                     wait.until(ExpectedConditions.visibilityOfElementLocated(locator));

                     logger.info("元素位置"+locator+"可见");

                     webElement =  driver.findElement(locator);

                     webElement.isEnabled();

              } catch (Exception e) {

                     e.printStackTrace();

                     logger.error("元素位置"+locator+"不可见"+e.getMessage());

                     }

              return webElement;

       }

      

       /**

        * 找到元素并点击操作

        * @param elment 传参为By元素locator

        */

       public void FindElementClick(By locator){

              WebElement element = driver.findElement(locator);

              if(element != null){

                     element.click();

                     logger.info("元素点击操作,位置"+locator);

              }     

       }

      

       /**

        * 找到元素并输入内容

        * @param elment 传参为By元素locator

        * @param text  传参输入的内容

        */

       public void FindElementSendKeys(By locator,String text){

              WebElement element = driver.findElement(locator);

              if(element != null){

                     element.clear();

                     element.sendKeys(text);

                     logger.info("元素输入内容"+text);

              }     

       }

      

       /**

        *找到元素并取出元素文本值返回

        * @param elment  传参为By元素locator

        * @return  返回文本值,没有文本值返回null

        */

       public String FindElementGetText(By locator){

              String result = null;

              WebElement element = driver.findElement(locator);

              if(element != null){

                     result = element.getText();

                     logger.info("获取元素文本值:"+result);             

              }                          

              return result;

       }

}

上面代码仅仅列出Click、sendKey、getText三个方法,三个方法都加了显示等待的处理,并使用isEnabled()方法处理,元素可见并可用时方操作,保证了操作元素的强壮性;其他方法就不列出了。Driver的获取是通过ThreadLocal管理,实现多线程运行。

2.新建LoginPage类,是登录页的元素管理,继承基类使用基类封装的操作方法。

public class LoginPage extends BasePage{

 

       private static Logger logger = Logger.getLogger(LoginPage.class);

      

       /**

        * 用户名输入框

        * @param username

        */

       public void usernameTextBox(String username){

              FindElementSendKeys(By.id("username"),username);

              logger.info("登录用户名输入:"+username);

       }

      

       /**

        * 密码输入框

        * @param pw

        */

       public void passwordTextBox(String pw){

              FindElementSendKeys(By.id("userpwd"),pw);

              logger.info("登录密码输入:"+pw);

       }

      

       /**

        * 登录按钮

        */

       public void login_Button(){

              FindElementClick(By.name("button"));

              logger.info("点击登录按钮");

       }

      

      

       /**

        * 登录失败时的提示语

        * @return

        */

       public String error_Warn(){

              String errorWarn = FindElementGetText(By.cssSelector(".content"));

              logger.info("登录失败提示语:"+errorWarn);

              return errorWarn;

       }

 

       /**

        * 登录操作

        * @param list 入参参数

        * @return  返回验证的结果

        */

       public String login(List<String> list){

              String act = null;

              String startTitle = WebDriverMethod.ThreadDriver.get().getTitle();

              usernameTextBox(list.get(0));

              passwordTextBox(list.get(1));

              login_Button();

              String stopTitle = WebDriverMethod.ThreadDriver.get().getTitle();

              if(startTitle.equals(stopTitle)){

                     act = error_Warn();

                     logger.info("登录失败,页面停留在本页,错误内容:"+act);

              }else{

                     act = "登录成功";

              }

              return act;

       }

}

    通过上面的LoginPage可以看出,每个元素都独立管理,使用元素直接调用即可;login作为登录操作方法,入参是list集合主要是解决多参数的场景;LoginPage在POM中称为pageObject页面对象,当元素定位路径有改变只需要单独改变元素的定位,这完全符合java的封装思想也达到了解耦的效果。

二、PageFactory

    对于POM模式,selenium提供了Selenium PageFactory,意思就是页面工厂模式;主要也是对元素进行分离管理;使用方法是@FindBy注解描述元素定位,如:

@FindBy(id=”username”) WebElement username

@FindBy(xpath=”//*[@id=’username’]”) WebElement username

    同样的也是支持id、css、xpath、className、tagName等元素定位方式的,这里就不一一举例啦。使用注解只是描述元素位置,并没有发现使用findElement方法定位实例化WebElement对象。而是要使用PageFactory.initElements(driver,page)方法来初始化实例化WebElement。调用initElements()有两种方法,建议加上隐式等待的的方式。

(1)PageFactory.initElements(driver, XXX.class);

(2)PageFactory.initElements(new AjaxElementLocatorFactory(driver, 10) ,XXX.class);

public class LoginPage {

      

       public WebDriver driver;

 

       @FindBy(id="username")WebElement username;

      

       @FindBy(id=" userpwd ")WebElement password;

      

       @FindBy(name="button")WebElement loginButton;

      

       LoginPage(WebDriver driver){

              this.driver = driver;

              PageFactory.initElements(new AjaxElementLocatorFactory(driver,10 ),this);

       }

      

       public void login(String uname,String pw){

              username.sendKeys(uname);

              password.sendKeys(pw);

              loginButton.click();

       }

}

三、POM模式优点

1.首先来两种看看使用POM的好处:

(1)每个元素的管理是独立,修改的时候只修改单个元素的定位

(2)元素与操作是分离的,修改元素不影响逻辑操作

(3)代码美观、可读性提高

2.自定义POM与selenium提供的PageFactory的区别

首先自定义POM模式自由,可根据自己要求编写逻辑与判断;而对于PageFactory初始化时只提供了隐式等待,但这不能说PageFactory就处于劣势;那到底使用谁更好呢?下面来见分晓。

四、PageFactory优势

    首先PageFactory的原理是通过反射以及代理方式实现,当使用@FindBy注解描述元素定位,如@FindBy(xpath=”//*[@id=’username’]”)这只是描述元素位置,并没有发现使用findElement方法定位实例化WebElement对象。而是当使用PageFactory.initElements(driver,page)方法时才会通过动态代理、反射去调用findElement()方法实例化WebElement对象。而为什么叫动态代理呢?我们平时使用的findElement()方法实例化WebElement对象时,在调用这个对象才会执行,而这带来的坏处就是如果调用这个对象后,页面DOM刷新后,不重新再使用findElement()方法查找就会报错。而使用Factory工厂模式的proxy之后每次调用都会自动查找,这样就减少很多代码。下面写段代码演示一下它的强大之处:

1.新建一个html文件,随意取名为1.html

<html>

<head>

<script type="text/javascript">

function reloadPage()

  {

  window.location.reload()

  }

</script>

</head>

 

<body>

<input type="button" value="点击我"

onclick="reloadPage()" name="button"/>

</body>

</html>

    Html代码,点击按钮dom重新去请求服务器拿文件加载,当然这种操作在实际使用中是很少用的,因为会造成页面加载慢,影响用户体验。要说到dom重新加载为什么会影响页面加载?这又涉及到浏览器工作,简单的说就是响应拿到html,dom树解析形成一个架子,然后遇到图片,去请求一次图片地址得到响应后显示,遇到javascript执行javascript;如果这时候有个按钮点击会触发dom的reload()事件,好啦,之前做的都白干了,重头再来一遍,又开始解析html形成dom树,然后继续往下。这样耗时可想而之。下面主要是为了演示,不代表实际就真的会出现,但不排除被使用,不然reload()事件就没有任何存在的价值了。

2.简单的findElement()查找,自定义的POM模式与也是使用findElement()这种方式去定位实例化WebElement。

public class Animal {

       public static void main(String[] args) {

              WebDriver driver = new ChromeDriver();

              driver.get("file:///D:/1.html");

              WebElement element =driver.findElement(By.name("button"));

              System.out.println(element.getAttribute("value"));

              element.click();

              System.out.println(element.getAttribute("value"));

              driver.quit();

       }

}

运行结果,报错,大概意思就是旧的元素,在新页面中找不到啦。

Exception in thread "main" org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document

3.使用PageFactory注解的方式:

public class Animal {

 

       @FindBy(name="button")

       public static WebElement test;

 

       public static void main(String[] args) {

              WebDriver driver = new ChromeDriver();

              driver.get("file:///D:/1.html");

              Animal animal = PageFactory.initElements(driver, Animal.class);

              System.out.println(test.getAttribute("value"));

              test.click();

              System.out.println(test.getAttribute("value"));

              driver.quit();

}

}

运行结果全部正确,打印两个“点击我”文字。所以这就能很明显的体现出PageFactory的优势啦。但是,又要但是了,因为dom重载在实际使用中并不多,所以使用哪一种根据自己喜爱决定啦,我个人比较偏爱自定义,一个喜欢自由的人;所以对于PageFactory的代码讲解比较少,使用PageFactory可以自己修改代码,有问题随时交流。

 

猜你喜欢

转载自blog.csdn.net/ouyanggengcheng/article/details/84988533