当前位置: 首页 > 面试题库 >

如何实现可返回不同PageObjects的WebDriver PageObject方法

席波娃
2023-03-14
问题内容

我刚刚开始使用WebDriver,并且正在尝试学习最佳实践,尤其是使用PageObjects和PageFactory。

据我了解,PageObjects应该公开网页上的各种操作,并将WebDriver代码与测试类隔离。通常,根据所使用的数据,相同的操作可能导致导航到不同的页面。

例如,在这种假设的登录方案中,提供管理员凭据将带您进入AdminWelcome页面,提供客户凭据将带您进入CustomerWelcome页面。

因此,实现此目的的最简单方法是公开两个返回不同PageObjects的方法…

登录页面对象

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的每个排列公开许多不同的方法,这是惯例吗?


问题答案:

Bohemian的答案并不灵活-
您不能执行返回到同一页面的页面操作(例如输入错误的密码),也不能拥有多个页面操作导致不同的页面(如果您想弄乱了,登录页面还有另一项操作,导致结果不同)。最后,您还会堆满更多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

在每种情况下都有单独的方法也可以使LoginPageObject的API非常清晰-
而且很容易分辨出所有登录结果。使用接口限制使用该login()方法的页面时,我没有看到任何价值。

我同意汤姆·安德森(Tom
Anderson)的观点,应将可重用的WebDriver代码重构为细粒度的方法。是否对它们进行细粒度的暴露(以便测试类可以选择相关的操作),或者将它们作为单个粗粒度的方法组合并暴露给测试类,可能是个人喜好问题。



 类似资料:
  • 我刚刚开始使用WebDriver,我正在尝试学习最佳实践,特别是使用PageObject和PageFactory。 我的理解是,PageObject应该公开网页上的各种操作,并将WebDriver代码与测试类隔离开来。根据所使用的数据,相同的操作通常会导致导航到不同的页面。 例如,在这个假设的登录场景中,提供管理员凭据将带您进入AdminWelcome页面,提供客户凭据将带您进入CustomerW

  • 问题内容: 可以说我有一个叫做Guice服务的服务,这是它的构造函数 我的代码曾经使用枚举创建它 而且我必须在某处进行工厂实施。像这样 现在,我想使用Guice,我想我想使用FactoryModuleBuilder。 如果我对IPayment的了解不止一种,该怎么办? (例如,CardPayment,CashPayment) 适用于一个 .implement(IPayment.class, Cas

  • 我有一个接口 创建 有两种方法,一种是异步的,一种是同步的。我想为这两个方法中的每一个提供一个接口实现。 对于异步方法,其实现可能如下所示: 但是我应该如何实现使用同步方法创建 的类 呢? 我可以实现方法以同步运行: 然后,编译器将警告不要在方法签名中使用 : 此异步方法缺少'await'运算符,将同步运行。考虑使用'await'运算符等待非阻塞API调用,或'await task.run(...

  • 问题内容: 今天,我偶然发现了一些我什至没想到可以编译的Java代码。减少到最低限度,它看起来像这样: 乍一看,类型参数的的方法和期待,因为没有必要,不使用其他任何地方。无论如何,我发现这在允许冲突的返回值类型共存于同一实现中起着至关重要的作用:如果忽略了一个或两个,则代码不会编译。这里是非工作版本: 我不需要修复上面的代码片段,因为这些只是我用来解释我观点的示例。我只是想知道为什么编译器对它们的

  • 我已经阅读了很多Java8可选版本,我确实理解这个概念,但在我的代码中尝试实现它时仍然会遇到困难。 虽然我在网上搜索了一些好的例子,但我没有找到一个解释得很好的例子。 我有下一个方法: 这个简单的方法通过传递文件路径来返回文件的md5。正如您所注意到的,如果文件路径不存在(或键入错误),将抛出NoSuchFileException,该方法返回Null。 我希望使用Optional,而不是返回nul

  • 该方法应返回类型Animal的值。如果出现问题,我想指出存在错误,正确的技术是什么?以防我不想出错。我可以返回什么来代替动物类型?