记录自己看Selenium源码的一些收获(二)- PageObject

记录自己看Selenium源码的一些收获(二)- PageObject,第1张

前段时间刚看完大话设计模式,感觉又对设计模式有了一点点深的理解,这次顺便把selenium的PageObject好好回顾一下。


PageObject:我对原文的浅显理解,它是用到了编程中的面对对象的思想。


官方文档如下:

Page object models | Seleniumhttps://www.selenium.dev/documentation/test_practices/encouraged/page_object_models/我们所用到的定位元素, *** 作元素等方法应该封装它对应的page类里面,而不是写在测试代码里

page object is an object-oriented class that serves as an interface to a page of your AUT. The tests then use the methods of this page object class whenever they need to interact with the UI of that page. The benefit is that if the UI changes for the page, the tests themselves don’t need to change, only the code within the page object needs to change. Subsequently all changes to support that new UI are located in one place.

他的最大好处是当元素的样式等发生了改变,我们只需要修改page类里面的方法 还有更重要的一个好处是,这些方法都可以得到很好复用。


拿我现在做的项目来说,我们的page文件夹下的类就是按照界面的菜单页面的树级结构来存放,要使用很方便。



官方文档上的常用原则

虽然selenium官方文档中有说到我们可以自由拓展用法,但是还是有一些通用法则,现在将文档中用到的一些法则贴在下面,加上了一些自己的解释

  • The public methods represent the services that the page offers
  • Try not to expose the internals of the page
  • Generally don’t make assertions
  • Methods return other PageObjects
  • Need not represent an entire page
  • Different results for the same action are modelled as different methods
  • 我们在page里面写的方法代表了这个PAGE可以给测试提供的服务
  • 不要暴露页面内部,我们既然把页面当作服务提供对象,那么要尽量避免暴露webdriver对象
  • 不要在页面里面写断言,这一部分需要在测试代码中进行,要是真的有断言的时候,也只能出现在验证这个页面是否真的跳转成功,这个部分经常在构造函数当中进行
  • 在涉及会到跳转页面的方法( *** 作)中,可以返回其他页面的对象,我们非常鼓励这么做,这样可以体现页面之间的关系。


  • 页面不需要代表整个页面,里面可能会有其他(组件)Page Component Object,这些组件可以放在pageobject当中,涉及到更复杂的页面,还可以有嵌套的页面组件
  • 在一个页面中可以写不同方法,应对 *** 作不同的情况(比如登录失败,登录成功,虽然是同样的 *** 作,但是可以返回不同的页面(又或者是捕捉异常)

回顾一下,我发现我所做的项目里面有违背了几个原则:

  • 没有在跳转页面的方法中返回其他页面对象,这样没有很好的体现页面的关系,而是靠自己初始化一个实例
  • 需要加强复杂页面需要设计层级的页面结构
  • 几乎没有捕捉异常的情况,以及处理 *** 作发生的机制
  • 几乎没有重写Page的构造方法,进行一些check等等

不过因为项目是半路接手的,也就follow了之前的rule,以后会更加注意。



PageFactory

既然说到了面向对象,我们很容易想到了一个很常用的设计模式,即:工厂模式。


一种实例化对象的设计模式,我们到底要实例化哪些页面,以后我增加了更多页面要去实例化怎么办?

我们自动化的代码并没有很强的逻辑关系(比如要通 过很复杂的逻辑关系决定实例化哪一个类)

所以在我们的Scala 自动化项目中是这样落地的。


  • 通过定义了一个页面注册(比如allPages)类 并且在加载spec时,一次性实例化该类,生成所有page的实例(需要lazy关键字)
    • 因为Scala中有lazy关键字(不确定java是不是有同样的代码)在引用page类型的变量前加lazy能在延迟实例化,这样不会在每次初始化页面注册类时占用太多资源
    • 在测试框架(一般是我们写的代码的Spec的超类)的构造方法中去初始化AllPage
  • 所有的Page页面需要继承同样的父类BasePage类
  • 新添加的page需要在allPages的构造方法中加上初始化该对象的代码。


  • 使用的时候用 AllPage.mainPage.sendKeyWords("333");
  • 我们初始化的时候隐藏传入Driver变量,是因为项目有DSL,重新了Selenium的寻找元素方法(以后有机会再把过程写一下)

// 注册类
public class AllPages
{
    lazy var loginPage = new loginPage;
    lazy var mainPage = new mainPage;

}

public class loginPage extends Page
{

// 元素代码

}

public MySpec extends BaseSpec{

// 其他代码

val allpage:AllPages  = new AllPages;
}

Selenium项目中的PageFactory类

这个类中initElement方法还涉及到这三个类FieldDecorator ElementLocatorFactory pageClassToProxy 需要再仔细研究下这几个类的用法~

下面是一个简单的例子来使用该方法(需要配合FindBy 标记)一起使用,就不需要自己一个个写By方法了,还是很方便的

package Page;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;

public class MainPage extends BasePage {
// 用Findby标记寻找元素的方式以及value,元素在被调用一次时会重新查询
    @FindBy(id="kw")
    private WebElement inputField;

    public MainPage(WebDriver driver) {
        super(driver);
    }

    public void sendKeyWords(String keywords){
        inputField.sendKeys(keywords);
    }
}

package Page;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;

public class BasePage {
    private WebDriver driver;
    public BasePage(WebDriver driver){
        this.driver = driver;
// 使用这个方法初始化Page,需要传入page和driver
        PageFactory.initElements(driver, this);
    }

}

public static void main(String[] args) {
            //  init the driver
            System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
            WebDriver c = new ChromeDriver();
            c.get("https://www.baidu.com/");
            MainPage page = new MainPage(c);
// 使用page里面的方法
            page.sendKeyWords("333");
}


总结

PageObject已经是selenium项目中默认的规则了,它很好的体现了面向对象的思想

实际的项目中可以根据自己项目的要求来写很复杂的逻辑的Factory类(如果有必要)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/564373.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-06
下一篇 2022-04-06

发表评论

登录后才能评论

评论列表(0条)

保存