java - 初始化 Selenium 页面对象,最佳实践

标签 java selenium phantomjs

我被要求创建一个应用程序,使用 Java、Selenium 和 PhantomJS 填写表单并将其提交到第三方网站(我是一名实习生,所以所有这些对我来说都是相当新的)。

这将需要在大约十几个不同的页面之间跳转。我使用页面对象模型,每个页面都有不同的类。我的问题是:

初始化所有页面对象的最佳方法是什么?

我目前就是这样做的。我在设置过程中初始化了几个页面:

public class MyProject
{
    private HomepagePageObject homepagePageObject;
    private SecondpagePageObject secondpagePageObject;
    private ThirdpagePageObject thirdpagePageObject;
    private FourthpagePageObject fourthpagePageObject;
    private WebDriver driver;

    public MyProject()
    {
        driver = new PhantomJSDriver();
        driver.get("http://www.homepage.com");
        homepagePageObject = new HomepagePageObject(driver);
        homepagePageObject.clickButtonForSecondPage();
        secondpagePageObject = new SecondpagePageObject(driver);
        secondpagePageObject.clickButtonForThirdPage();
        thirdpagePageObject = new ThirdpagePageObject(driver);
    }
 }

某些页面在设置过程中无法访问(即确认提交的页面)。因此,每当我遇到可能已初始化或尚未初始化的页面时,我都会这样做:

    if(fourthpagePageObject == null)
    fourthpagePageObject = new FourthpagePageObject(driver);

这似乎是一种困惑的方法。当您不知道访问页面的顺序时,是否有初始化页面对象的“最佳实践”?

最佳答案

我们正在使用工厂类,如下所示:

class PagesFactory{
    private WebDriver driver;

    public HomePage getHomePage(){
         return new HomePage( driver );
    }

    // if you want to share the same object (singleton) among many tests

    private SecondPage secondPage;
    public SecondPage getSecondPage(){
         if( secondPage == null ){
              secondPage = new SecondPage( driver );
         }
         return secondPage( driver );
    }
    .....
    .....
}

在我们的项目中,我们使用 Spring 来实例化 WebDriver 并将其注入(inject)到许多工厂类中(我们有十几个工厂,每个工厂对我们应用程序不同部分的页面进行分组)。
在测试类中,我们简单地获取所需的工厂,并且当需要某些页面时,所有测试方法都简单地引用工厂对象。
在 Java 中,可以通过以下方式完成(但我们也使用 Spring 来实例化工厂并将工厂注入(inject)到我们的测试类中):

class TestSomePartOfApplication{
    KKKPages kkkPages = KKKPages.getInstance();
    AtosPages atosPages = AtosPages.getInstance();
    .....
    void testScenario1(){
       kkkPages.getPageA.fillField("Field name 1", 120 );
       kkkPages.getPageA.clickButtonOK();
       ......
    }

    void testScenario2(){
       kkkPages.getPageB.fillField("Field 15", "abc" );
       kkkPages.getPageA.clickButtonOK();
       atosPages.getPageC.fillField("Field 33", "Client name");
       ......
    }
}
<小时/>

既然你在问题中写道

Some pages cannot be accessed during setup (i.e. a page that confirms a submission). So everytime I come across a page that may or may not have been initialized, I do this:

所以我猜您正在使用一种导航策略,其中如果某些操作导航到这个新页面,则页面对象负责创建另一个页面的新页面对象。这是随处可见的常见建议,您可以在许多与页面对象模式相关的文章中找到此策略的示例,通常是登录页面的经典示例:

class LoginPage{

    public HomePage loginUaer( String user, String password ){
        // fill in fields and click Login button
        .....
        .....
        return new HomePage( .... );
    }
}

但我们发现,在具有大量页面的复杂应用程序中很难遵循此策略,这些页面由许多共同元素组成,但彼此之间仅在一些小细节上有所不同。这种方法增加了项目的复杂性,因为许多类必须相互耦合,而且页面对象在很多情况下必须是有状态的,并且必须记住以前在页面上执行的操作,或者必须使用一些其他方法(检查页面)/窗口内容?)来决定是否可以进行导航,以及哪种导航(例如 - 当用户输入错误的密码时,会显示一个错误消息页面,如果他将字段留空,则会显示另一页面,否则如果用户名和密码正确,则转发到主页等)。为此,必须在页面对象中实现一些复杂的规则,模仿应用程序的行为 - 这是非常费力且容易出错的。
我们正在使用另一种策略,如下所述:
http://martinfowler.com/bliki/PageObject.html

Having page objects be responsible for creating other page objects in response to things like navigation is common advice. However some practitioners prefer that page objects return some generic browser context, and the tests control which page objects to build on top of that context based on the flow of the test (particularly conditional flows). Their preference is based on the fact that the test script knows what pages are expected next and this knowledge doesn't need to be duplicated in the page objects themselves. They increase their preference when using statically typed languages which usually reveal page navigations in type signatures.

这种方法对我们来说非常有效。我们有很多独立的、解耦的、无状态的类(POJO),它们在页面上实现非常简单的操作——甚至只在页面的某些部分(节)上实现。想象一下这样一种情况,有数百个页面共享公共(public)元素(例如客户列表、客户数据部分、附件部分、消息部分等),并且它们彼此之间仅略有不同(一个页面)有 A、B、C 部分,另一部分有 B、C、D 等)。为每个页面创建单独的页面对象(数百个类 - 这是一场噩梦)是不切实际的。对于每个这样的部分,我们只有几个独立的页面对象,并且在测试本身中实现了导航流程,如下所示:

pageFactoryA.getSectionAPage().clickButtonB();
pageFactoryB.getSectionBPage.fillInCustomerField( customerName );
pageFactoryA.getSectionCPage.clickButton("Display customer report");
// the above operation should display a new page: Customer Report
// so we get a CustomerReportPage from the factory
int total = pageFactoryC.getCustomerReportPage().getTotal();
assertEquals( total, 2000 );
.....

关于java - 初始化 Selenium 页面对象,最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37220008/

相关文章:

java - JFrame 在图标化时监听 KeyEvent

javascript - 是否可以使用 javascript 读取 something.properties ? (在 .hta 文件中)>> 读取 Selenium

java - Selenium java调用javascript函数

javascript - Selenium WebDriver + PhantomJS + Python - 执行脚本和处理对话框

javascript - 幻影;单击一个元素

java - 为什么抽象属性在 Runnable 接口(interface)的子类中丢失?

java - 删除双向递归关系的最简单方法?

java - Jsoup对特定表数据的处理

java - 使用 selenium/java 生成随机字符并发送到文本字段

phantomjs - Yeoman 生成器总是出错