我刚刚开始使用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()
andloginAsCustomer()
方法并通过调用泛型替换它们的内容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 代码并为每个数据/页面对象的排列公开许多不同的方法是常见的做法吗?