如何实现的webdriver PageObject方法,可以返回不同PageObjects(How

2019-07-03 19:28发布

我刚开始使用webdriver的 ,而我努力学习的最佳实践,特别是使用PageObjects和PageFactory 。

这是我的理解是PageObjects应公开在网页上的各种操作,并隔离测试类的代码的webdriver。 很多时候,同样的操作会导致导航到取决于所使用的数据不同的页面。

例如,在这个假设的情况下登录,提供管理员凭据带你到AdminWelcome页面,并提供客户证书带你到CustomerWelcome页面。

因此,要实现这个最简单的方法就是公开两个方法,返回不同PageObjects ...

登录PageObject

package example;

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

public class Login {

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

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

    @FindBy(id = "submitButton")
    private WebElement submitButton;

    private WebDriver driver;

    public Login(WebDriver driver){
        this.driver = driver;
    }

    public AdminWelcome loginAsAdmin(String user, String pw){
        username.sendKeys(user);
        password.sendKeys(pw);
        submitButton.click();
        return PageFactory.initElements(driver, AdminWelcome.class);
    }

    public CustomerWelcome loginAsCustomer(String user, String pw){
        username.sendKeys(user);
        password.sendKeys(pw);
        submitButton.click();
        return PageFactory.initElements(driver, CustomerWelcome.class);
    }

}

而在做测试类中的以下内容:

Login loginPage = PageFactory.initElements(driver, Login.class);
AdminWelcome adminWelcome = loginPage.loginAsAdmin("admin", "admin");

要么

Login loginPage = PageFactory.initElements(driver, Login.class);
CustomerWelcome customerWelcome = loginPage.loginAsCustomer("joe", "smith");

替代方法

而不是重复代码的,我希望有暴露一个更清洁的方式login()其返回的相关PageObject方法。

我想到了创建的页面层次结构(或让他们实现一个接口),这样我可以使用它作为返回类型,但感觉笨拙。 我想出了以下内容:

public <T> T login(String user, String pw, Class<T> expectedPage){
    username.sendKeys(user);
    password.sendKeys(pw);
    submitButton.click();
    return PageFactory.initElements(driver, expectedPage);
}

这意味着你可以做测试类如下:

Login loginPage = PageFactory.initElements(driver, Login.class);
AdminWelcome adminWelcome = 
    loginPage.login("admin", "admin", AdminWelcome.class);

要么

Login loginPage = PageFactory.initElements(driver, Login.class);
CustomerWelcome customerWelcome = 
    loginPage.login("joe", "smith", CustomerWelcome.class);

这是灵活的-你可以添加一个ExpiredPassword网页,而不必改变login()在所有的方法-添加另一测试,并通过在适当的过期证书和ExpiredPassword页面预期的页面。

当然,你可以很容易离开loginAsAdmin()loginAsCustomer()方法,并以通用的调用替换它们的内容login()这将随后进行私人)。 一个新的页面(如ExpiredPassword页),那么就需要另一种方法(如loginWithExpiredPassword()

这样做的好处是,方法名称实际上意味着什么(你可以很容易地看到,有登录的3个可能的结果),该PageObject的API是有点更容易使用(没有“的网页”来传递),但的webdriver代码仍然被重用。

进一步改进...

如果你没有露出单一login()方法,你可以使它更明显的页面可以通过添加一个标记接口,这些网页登录即可到达(如果你暴露每个场景的方法这可能不是必要的)。

public interface LoginResult {}

public class AdminWelcome implements LoginResult {...}

public class CustomerWelcome implements LoginResult {...}

而更新的登录方法:

public <T extends LoginResult> T login(String user, String pw, 
    Class<T> expectedPage){
    username.sendKeys(user);
    password.sendKeys(pw);
    submitButton.click();
    return PageFactory.initElements(driver, expectedPage);
}

这两种方法似乎运作良好,但我不知道它是如何扩展为更复杂的情况。 我还没有看到任何代码示例喜欢它,所以我不知道其他人一样,当一个页面上的操作可能会导致取决于数据不同的结果呢?

或者是通常的做法是只复制webdriver的代码和揭露很多不同的方法进行数据/ PageObjects的每个排列?

Answer 1:

波西米亚的答案是不灵活 - 你不能有一个页面动作并返回到相同的页面(如输入错误的密码),也可以有产生不同的页面超过100次的行动(认为你有什么乱七八糟的,如果登录页面有导致不同结果的另一个动作)。 您还落得堆更多PageObjects只是为了迎合不同的结果。

试运行这一些(和包括失败的登录情况)后,我已经谈妥了以下几点:

private <T> T login(String user, String pw, Class<T> expectedPage){
    username.sendKeys(user);
    password.sendKeys(pw);
    submitButton.click();
    return PageFactory.initElements(driver, expectedPage);
}

public AdminWelcome loginAsAdmin(String user, String pw){
    return login(user, pw, AdminWelcome.class);
}

public CustomerWelcome loginAsCustomer(String user, String pw){
    return login(user, pw, CustomerWelcome.class);
}

public Login loginWithBadCredentials(String user, String pw){
    return login(user, pw, Login.class);
}

这意味着您可以重复登录逻辑,但避免需要测试类在预期的页面,这意味着测试类传递是非常具有可读性:

Login login = PageFactory.initElements(driver, Login.class);
login = login.loginWithBadCredentials("bad", "credentials");
// TODO assert login failure message
CustomerWelcome customerWelcome = login.loginAsCustomer("joe", "smith");
// TODO do customer things

有每个方案的不同的方法也使得Login PageObject的API非常明确的-它很容易分辨所有日志记录的成果中,我没有看到在使用接口来限制与使用的页面的任意值。 login()方法。

我和汤姆·安德森同意,可重用代码的webdriver应重构为细粒度的方法。 无论它们暴露精细粒度(所以测试类可以随意挑选相关操作),或合并,并暴露在测试类作为一个单一的粗晶的方法可能是个人喜好的问题。



Answer 2:

您正在使用多种类型的污染你的API - 只使用泛型和继承:

public abstract class Login<T> {

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

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

    @FindBy(id = "submitButton")
    private WebElement submitButton;

    private WebDriver driver;

    private Class<T> clazz;

    protected Login(WebDriver driver, Class<T> clazz) {
        this.driver = driver;
        this.clazz = clazz
    }

    public T login(String user, String pw){
        username.sendKeys(user);
        password.sendKeys(pw);
        submitButton.click();
        return PageFactory.initElements(driver, clazz);
    }
}

然后

public AdminLogin extends Login<AdminWelcome> {

   public AdminLogin(WebDriver driver) {
       super(driver, AdminWelcome.class);
   }
}

public CustomerLogin extends Login<CustomerWelcome> {

   public CustomerLogin(WebDriver driver) {
       super(driver, CustomerWelcome.class);
   }
}

等所有类型的登录页面


注意变通为类型擦除的能的一个实例通过Class<T>PageFactory.initElements()方法,通过使类的实例入构造,其被称为“类型令牌”图案。



文章来源: How to implement WebDriver PageObject methods that can return different PageObjects