2010-10-20 6 views
11

J'aime vraiment comment le sélénium 2 par convention vous pousse à utiliser PageObjects en tant que POJO, puis en utilisant simplement PageFactory pour instancier les champs de cette classe. Ce que je trouve limitant est que nous réutilisons beaucoup d'éléments sur de nombreuses pages différentes. Le gros problème est que ces composants réutilisés n'ont pas le même identifiant/nom quand ils apparaissent sur des pages différentes; Cependant, les tests que nous ferions pour chacun d'entre eux sont les mêmes. Par exemple, nous collectons des dates dans de nombreux endroits. Ainsi, un objet page d'exemple pour ce qui pourrait être (mois, les champs de jour enlevés):Selenium Page Object Réutilisation

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

alors je pourrais simplement tester cela avec le code ci-dessous:

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

Ce que je voudrais vraiment faire est une configuration par laquelle Spring peut injecter l'identifiant/nom/xpath, etc ... dans l'application.

Y a-t-il un moyen de le faire, sans perdre la possibilité d'utiliser PageFactory?

Édition 1 - Ajout de classes de niveau de base idéales, travaillant sur les localisateurs personnalisés et les usines.

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

Edit 2 - Tout comme une note, aussi longtemps que vous utilisez Sélénium 2.0.a5 ou plus, vous pouvez maintenant donner au conducteur une valeur de délai d'attente implicite.

Vous pouvez remplacer votre code avec:

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

Répondre

15

Vous pouvez construire votre objet des éléments Web commun (juste inventé ce nom :)) - chaque CWE représentera un « widget » qui est utilisé sur différentes pages. Dans votre exemple, il s'agira d'une sorte de widget Date: il contient l'année, le mois et un jour. Fondamentalement, ce sera un objet de page.

PageFactory requiert les constantes de chaîne à utiliser dans les annotations @FindBy.

Pour résoudre cette limitation, nous avons créé notre propre ElementLocator s.

Vous pouvez utiliser le DateWidget dans votre test:

.... 
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 classe DateWidget, qui contient des localisateurs personnalisés et parseurs annotation est:

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

Cela m'a certainement obtenu sur la bonne piste. J'espérais l'étendre plus loin que juste les ID (vous pouvez injecter tous les moyens nécessaires et ensuite localiser). Mon problème était que je ne voyais pas comment ces Fields étaient créés, ce qui ne me permettait pas de bien saisir l'ElementLocator. – Scott