2010-10-20 15 views
11

Me gusta mucho cómo selenio 2 por convención lo empuja hacia el uso de PageObjects como POJO, y luego simplemente usa PageFactory para crear instancias de los campos en esta clase.Página de Selenio Reutilización de objetos

Lo que estoy encontrando limitante es que reutilizamos muchos elementos en muchas páginas diferentes. El gran problema es que estos componentes reutilizados no tienen el mismo ID/nombre cuando aparecen en páginas diferentes; sin embargo, las pruebas que correríamos para cada una de ellas son las mismas.

Como ejemplo recopilamos fechas en muchos lugares. Por lo tanto un objeto ejemplo, la página de esto podría ser (mes, día campos eliminados):

public class DatePageObject { 
    private WebDriver driver; 

    DatePageObject(WebDriver driver) { 
     this.driver = driver; 
    } 

    @FindBy(id = "someIdForThisInstance") 
    private WebElement year; 

    public void testYearNumeric() { 
     this.year.sendKeys('aa'); 
     this.year.submit(); 
     //Logic to determine Error message shows up 
    } 
} 

Entonces yo podría simplemente probar esto con el código de abajo:

public class Test { 
    public static void main(String[] args) { 
     WebDriver driver = new FirefoxDriver(); 
     DatePageObject dpo = PageFactory.initElements(driver, DriverPageObject.class); 
     driver.get("Some URL"); 
     dpo.testYearNumeric(); 
    } 
} 

Lo que realmente me gustaría hacer es tener una configuración mediante la cual con Spring puedo inyectar esa identificación/nombre/xpath, etc ... en la aplicación.

¿Hay alguna forma de que pueda hacer esto, sin perder la capacidad de utilizar PageFactory?

Editar 1 - Agregar las clases de nivel base ideales, trabajando en localizadores personalizados y fábricas.

public class PageElement { 
    private WebElement element; 
    private How how; 
    private String using; 

    PageElement(How how, String using) { 
     this.how = how; 
     this.using = using; 
    } 
    //Getters and Setters 
} 


public class PageWidget { 
    private List<PageElement> widgetElements; 
} 


public class Screen { 
    private List<PageWidget> fullPage; 
    private WebDriver driver; 

    public Screen(WebDriver driver) { 
     this.driver = driver; 
     for (PageWidget pw : fullPage) { 
      CustomPageFactory.initElements(driver, pw.class); 
     } 
} 

Edición 2 - Así como una nota, siempre y cuando se está ejecutando selenio 2.0.a5 o mayor, ahora se puede dar al conductor un valor de tiempo de espera implícita.

para que pueda reemplazar el código con:

private class CustomElementLocator implements ElementLocator { 
    private WebDriver driver; 
    private int timeOutInSeconds; 
    private final By by; 


    public CustomElementLocator(WebDriver driver, Field field, 
      int timeOutInSeconds) { 
     this.driver = driver; 
     this.timeOutInSeconds = timeOutInSeconds; 
     CustomAnnotations annotations = new CustomAnnotations(field); 
     this.by = annotations.buildBy(); 
     driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS); //Set this value in a more realistic place 
    } 


    public WebElement findElement() { 
     return driver.findElement(by); 
    } 
} 

Respuesta

15

Usted puede construir su objeto de página de la Web Elements Común (acabo de inventar este nombre :)) - CWE cada uno representará un "widget" que se utiliza en diferentes páginas. En su ejemplo, este será un tipo de widget de fecha: contiene el año, el mes y un día. Básicamente será un Objeto de Página.

PageFactory requiere que las constantes de cadena se utilicen en las anotaciones @FindBy.

Para resolver esta limitación creamos nuestra propia ElementLocator s.

Usted puede utilizar el DateWidget en su prueba:

.... 
DateWidget widget = new DateWidget(driver, "yearId", "monthId", "dayId"); 
.... 

public void testYearNumeric() { 
     widget.setYear("aa"); 
     widget.submit(); 
     //Logic to determine Error message shows up 

     // ... and day 
     widget.setDay("bb"); 
     widget.submit(); 
     //Logic to determine Error message shows up 
    } 

La clase DateWidget, que contiene localizadores personalizados y programas de análisis de anotación es:

package pagefactory.test; 

import java.lang.reflect.Field; 

import org.openqa.selenium.By; 
import org.openqa.selenium.WebDriver; 
import org.openqa.selenium.WebElement; 
import org.openqa.selenium.support.FindBy; 
import org.openqa.selenium.support.PageFactory; 
import org.openqa.selenium.support.pagefactory.Annotations; 
import org.openqa.selenium.support.pagefactory.ElementLocator; 
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory; 
import org.openqa.selenium.support.ui.ExpectedCondition; 
import org.openqa.selenium.support.ui.Wait; 
import org.openqa.selenium.support.ui.WebDriverWait; 

public class DateWidget { 

    // These constants are used to identify that they should be changed to the actual IDs 
    private static final String YEAR_ID = "$YEAR_ID$"; 
    private static final String MONTH_ID = "$MONTH_ID$"; 
    private static final String DAY_ID = "$DAY_ID$"; 

    // Elements whose ids will be replaced during run-time 
    /** Year element */ 
    @FindBy(id = YEAR_ID) 
    private WebElement year; 

    /** Month element */ 
    @FindBy(id = MONTH_ID) 
    private WebElement month; 

    /** day element */ 
    @FindBy(id = DAY_ID) 
    private WebElement day; 

    // The ids of the elements 
    /** ID of the year element */ 
    private String yearId; 

    /** ID of the month element */ 
    private String monthId; 

    /** ID of the day element */ 
    private String dayId; 

    public DateWidget(WebDriver driver, String yearId, String monthId, 
      String dayId) { 
     this.yearId = yearId; 
     this.monthId = monthId; 
     this.dayId = dayId; 

     PageFactory.initElements(new CustomLocatorFactory(driver, 15), this); 
    } 

    public String getYear() { 
     return year.getValue(); 
    } 

    public void setYear(String year) { 
     setValue(this.year, year); 
    } 

    public String getMonth() { 
     return month.getValue(); 
    } 

    public void setMonth(String month) { 
     setValue(this.month, month); 
    } 

    public String getDay() { 
     return day.getValue(); 
    } 

    public void setDay(String day) { 
     setValue(this.day, day); 
    } 

    public void submit() { 
     year.submit(); 
    } 

    private void setValue(WebElement field, String value) { 
     field.clear(); 
     field.sendKeys(value); 
    } 

    private class CustomLocatorFactory implements ElementLocatorFactory { 
     private final int timeOutInSeconds; 
     private WebDriver driver; 

     public CustomLocatorFactory(WebDriver driver, int timeOutInSeconds) { 
      this.driver = driver; 
      this.timeOutInSeconds = timeOutInSeconds; 
     } 

     public ElementLocator createLocator(Field field) { 
      return new CustomElementLocator(driver, field, timeOutInSeconds); 
     } 
    } 

    private class CustomElementLocator implements ElementLocator { 
     private WebDriver driver; 
     private int timeOutInSeconds; 
     private final By by; 

     public CustomElementLocator(WebDriver driver, Field field, 
       int timeOutInSeconds) { 
      this.driver = driver; 
      this.timeOutInSeconds = timeOutInSeconds; 
      CustomAnnotations annotations = new CustomAnnotations(field); 
      this.by = annotations.buildBy(); 
     } 

     @Override 
     public WebElement findElement() { 
      ExpectedCondition<Boolean> e = new ExpectedCondition<Boolean>() { 
       public Boolean apply(WebDriver d) { 
        d.findElement(by); 
        return Boolean.TRUE; 
       } 
      }; 
      Wait<WebDriver> w = new WebDriverWait(driver, timeOutInSeconds); 
      w.until(e); 

      return driver.findElement(by); 
     } 
    } 

    private class CustomAnnotations extends Annotations { 

     public CustomAnnotations(Field field) { 
      super(field); 
     } 

     @Override 
     protected By buildByFromShortFindBy(FindBy findBy) { 

      if (!"".equals(findBy.id())) { 
       String id = findBy.id(); 
       if (id.contains(YEAR_ID)) { 
        id = id.replace(YEAR_ID, yearId); 
        return By.id(id); 
       } else if (id.contains(MONTH_ID)) { 
        id = id.replace(MONTH_ID, monthId); 
        return By.id(id); 
       } else if (id.contains(DAY_ID)) { 
        id = id.replace(DAY_ID, dayId); 
        return By.id(id); 
       } 
      } 

      return super.buildByFromShortFindBy(findBy); 
     } 

    } 

} 
+0

Esto definitivamente me ha conseguido en la pista correcta. Tenía la esperanza de extenderlo más allá de las ID's también (podría inyectar cualquier medio necesario y luego ubicarlo). Mi problema era que no estaba viendo cómo se estaban creando esos campos, lo que me estaba haciendo no captar correctamente el ElementLocator. – Scott

Cuestiones relacionadas