Cómo implementar los métodos PageObject de WebDriver que pueden devolver diferentes PageObjects

Acabo de empezar a usarWebDriver, y estoy tratando de aprender las mejores prácticas, en particular utilizandoObjetos de página yPageFactory.

Tengo entendido que PageObjects debería exponer las diversas operaciones en una página web y aislar el código de WebDriver de la clase de prueba. Muy a menudo, la misma operación puede resultar en navegar a diferentes páginas dependiendo de los datos utilizados.

Por ejemplo, en este hipotético escenario de inicio de sesión, proporcionar credenciales de administrador lo lleva a la página de AdminWelcome, y proporcionar credenciales de Cliente lo lleva a la página de CustomerWelcome.

Así que la forma más fácil de implementar esto es exponer dos métodos que devuelven diferentes PageObjects ...

Login 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);
    }

}

Y haz lo siguiente en la clase de prueba:

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

o

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

En lugar de duplicar el código, esperaba que hubiera una forma más limpia de exponer un solologin() Método que devolvió el PageObject relevante.

Pensé en crear una jerarquía de páginas (o hacer que implementen una interfaz) para poder usar eso como el tipo de retorno, pero me parece torpe. Lo que se me ocurrió fue lo siguiente:

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

Lo que significa que puedes hacer lo siguiente en la clase de prueba:

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

o

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

Esto es flexible: puede agregar una página de ExpiredPassword y no tener que cambiar lalogin() método en absoluto: solo agregue otra prueba y pase las credenciales caducadas apropiadas y la página ExpiredPassword como la página esperada.

Por supuesto, usted podría fácilmente dejar elloginAsAdmin() yloginAsCustomer() métodos y reemplazar sus contenidos con una llamada al genéricologin() (que luego se convertiría en privado). Una nueva página (por ejemplo, la página ExpiredPassword) requeriría otro método (por ejemplo,loginWithExpiredPassword()).

Esto tiene la ventaja de que los nombres de los métodos realmente significan algo (puede ver fácilmente que hay 3 resultados posibles de inicio de sesión), la API de PageObject es un poco más fácil de usar (no se "pasa una página"), pero WebDriver El código todavía se está reutilizando.

Futuras mejoras...

Si expusiste el singlelogin() método, puede hacer que sea más obvio a qué páginas se puede acceder desde el inicio de sesión agregando una interfaz de marcador a esas páginas (probablemente no sea necesario si expone un método para cada escenario).

public interface LoginResult {}

public class AdminWelcome implements LoginResult {...}

public class CustomerWelcome implements LoginResult {...}

Y actualice el método de inicio de sesión para:

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);
}

Cualquiera de los dos enfoques parece funcionar bien, pero no estoy seguro de cómo escalaría para escenarios más complicados. No he visto ningún ejemplo de código como este, por lo que me pregunto qué hacen los demás cuando las acciones en una página pueden generar resultados diferentes según los datos.

¿O es una práctica común simplemente duplicar el código de WebDriver y exponer muchos métodos diferentes para cada permutación de datos / PageObjects?

Respuestas a la pregunta(2)

Su respuesta a la pregunta