2012-02-28 52 views
4

我想從這個獲得:如何實現@FindBy註釋的用戶類型?

@FindBy(xpath = "//div/span/img") 
public WebElement addNew; 

@FindBy(xpath = "//tr[2]/td[12]") 
public WebElement save; 

@FindBy(xpath = "//td/div/input") 
public WebElement entryIdel; 

@FindBy(xpath = "//textarea") 
public WebElement authorFieldel; 

@FindBy(xpath = "//td[3]/div/textarea") 
public WebElement titleFieldel; 

說:

@FindBy(xpath = "//div/span/img") 
public Button addNew; 

@FindBy(xpath = "//tr[2]/td[12]") 
public Button save; 

@FindBy(xpath = "//td/div/input") 
public InputBox entryIdel; 

@FindBy(xpath = "//textarea") 
public InputBox authorFieldel; 

@FindBy(xpath = "//td[3]/div/textarea") 
public InputBox titleFieldel; 

我以前創建的類爲每個元素,但當然沒有任何反應。我如何創建我的元素類,以便我可以使用它而不是WebElement?

這裏的InputBox在這一刻代碼:

import org.openqa.selenium.WebElement; 

    public class InputBox { 

protected WebElement element; 

public WebElement getElement() { 
    return element; 
} 

public InputBox(WebElement element) { 
    this.element = element; 
    // TODO Auto-generated constructor stub 
} 

public void type(String input) { 
    clearText(); 
    element.sendKeys(input); 
} 

public void clearText() { 
    element.clear(); 
} 

public boolean isEditable() { 
    return element.isEnabled(); 
} 

String getText() { 
    return element.getText(); 
} 

String getValue() { 
    return element.getValue(); 
} 

} 
+0

我發現了這個決議:看到那裏https://github.com/oraz/selenium – Arthur 2012-02-29 10:09:58

回答

6

創建一個新的實現FieldDecorator的。

當你使用你可能調用

public static void initElements(ElementLocatorFactory factory, Object page) 

的PageFactory這將成爲

public static void initElements(FieldDecorator decorator, Object page) 

你FieldDecorator能表現類似的DefaultFieldDecorator除了在您的自定義類型包裹代理。

見類在這裏[source]

+0

謝謝,我一直在爲此尋找。但我仍然無法意識到這一點。我打電話給PageFactory.initElements(driver,this);但我不知道如何打電話給我的自定義裝飾師 – Arthur 2012-02-28 14:28:35

+0

我還沒有測試過這個,但這是我想象中的課程。 http://pastebin.com/1ufk2n8Z你會用PageFactory.initElements(新的CustomFieldDecorator(新的DefaultElementLocatorFactory(驅動程序))調用它,這個) – roby 2012-02-28 19:07:26

+0

好吧,這很清楚,但應該看起來像我的用戶類?必須是內部特別的東西? – Arthur 2012-02-29 09:10:00

0

第一個猜測:你一直在想更好地命名convenion。在我的課堂上,按鈕看起來是這樣的:

private WebElement loginButton; 

在我硒測試中,我發現,更好的方法是有級的每一頁,如:

public Class LoginPage{ 
    private WebElement loginButton; 
    private WebElement loginField; 
    private WebElement passwordField; 
    private WebDriver driver; 

    public LoginPage(WebDriver drv){ 
    this.driver = drv; 
    } 

    public void login(String uname; String pwd){ 
    loginButton = driver.findElement(By.xpath("//td/div/input")); 
    passwordField = driver... 
    loginField = driver... 
    loginField.sendKeys(uname); 
    passwordField.sendkeys(pwd);  
    loginButton.click(); 
    } 

    } 

然後測試長相像這樣:

public void testLogin(){ 
WebDriver driver = new FirefoxDriver(); 
driver.get("http://the-test-page.com/login.htm"); 
LoginPage loginPage = new LoginPage(driver); 
loginPage.login("username", "password"); 
} 

但假設,這並不爲你工作,還有我的兩個guseeses:

杉木ST,你可以從WebElement擴展:

public class Button extends WebElement{ 

,但你可能要甚至實現所有WebElement公共方法,如果你不使用它們

然後爲第二猜你可以給駕駛者和找到路徑構造

public class Button { 
private WebDriver driver; 
private WebElement button; 
private WebDriver driver; 

public Button(WebDriver driver, By by){ 
    this,driver = driver; 
    button = findElement(by); 
} 

,並調用將是:

Button loginButton = new Button(driver, By.xpath("//td/div/input")); 

BTW我這裏assumtion是,你正在使用webdriver的方法

編輯 我發現,這是WebElement接口。所以你可以這樣做:

public class WebButton implements WebElement{ 

但你必須實現接口WebElement的所有抽象方法。

不管怎麼說,當我做到了,它讓我這樣做註解我在其他類:

@FindBy(xpath = "//textarea") 
public WebButton testButton; 

,但我從來沒有使用過這種方法並不能保證它會做什麼...

順便說一句,如果感興趣的話,我貼我的實現,這裏的例子:

http://pastebin.com/STr15UQd

+0

很明顯。但我想通過@FindBy註釋來設置我的用戶類型(按鈕,輸入框等) – Arthur 2012-02-28 15:17:00

+0

我從來沒有使用過這種方法,但會稍微更新一下我剛纔測試的 – 2012-02-28 16:01:27

+0

編輯的結果。希望它能起作用 – 2012-02-28 16:06:32

6

我已經找到了有關H非常有趣的帖子ow @FindBy的工作原理以及如何在基於Selenium(WebDriver)的測試中使用FieldDecorator:http://habrahabr.ru/post/134462/

該文章的作者是РоманОразмагомедов(Roman Orazmagomedof)。

這裏我給出更多關於如何使用FieldDecorator的解釋。此外,我還將展示原始實現的擴展版本,並增加了一些功能,這些功能可以使用ExpectedCondition界面等待裝飾字段的準備就緒。

設定目標

大多數硒頁面對象模式的插圖使用WebElement接口來定義的頁面字段:

public class APageObject {  

    @FindBy(id="fieldOne_id") 

    WebElement fieldOne; 


    @FindBy(xpath="fieldTwo_xpath") 

    WebElement fieldTwo; 


    <RESTO OF THE Page IMPLEMENTATION> 

} 

我想:

a)一個頁面是一個更通用的容器,能夠將多種形式組合在一起。

b)使用普通java對象而不是WebElement接口來聲明頁面上的字段。

c)有一個簡單的方法來確定頁面上的元素是否可以使用。

例如:

public class PageObject { 

     private APageForm formA; 

     <OTHER FORMS DECLARATIONS > 

     public void init(final WebDriver driver) { 

      this.driver = driver; 

      formA = new APageForm()); 

      PageFactory.initElements(new SomeDecorator(driver), formA); 

       <OTHER FORMS INITIALIZATION> 

     } 

     <THE REST OF the PAGE IMPLEMENTATION> 

} 

凡APageForm看起來類似APageObject,但有一點區別 - 每個字段的形式是由專用的java類定義。

public class APageForm { 

    @FindBy(id="fieldOne_id") 

    FieldOne fieldOne; 



    @FindBy(xpath="fieldTwo_xpath") 

    FieldTwo fieldTwo; 

    <REST OF THE FORM IMPLEMENTATION> 

} 

有兩個比較重要的點要記住:

一)這種做法應該使用Selenium ExpectedCondition;

b)這種方法應該有助於分離「數據傳遞」和「數據斷言」之間的代碼。

  1. 公共接口元素{

    public boolean isVisible(); 
    
        public void click(); 
    
        public ExpectedCondition<WebElement> isReady(); 
    

    }

該接口應當延續像按鈕,鏈接,標籤等更復雜的元素例如:

public interface TextField extends Element { 

     public TextField clear(); 

     public TextField enterText(String text); 

     public ExpectedCondition<WebElement> isReady(); 

} 

每個元素應提供isReady()以避免使用Thread.sleep()。

的元件的每一個執行應延伸AbstractElement類:

public abstract class AbstractElement implements Element { 

     protected WebElement wrappedElement; 



protected AbstractElement (final WebElement el) { 

       this.wrappedElement = el; 

     } 

     @Override 

     public boolean isVisible() { 

       return wrappedElement.isDisplayed(); 

     }  

     @Override 

     public void click() { 

      wrappedElement.click();  

     }  

     public abstract ExpectedCondition<WebElement> isReady();  

} 

例如:

public class ApplicationTextField extends AbstractElement implements TextField { 

     public ApplicationTextField(final WebElement el) { 

       super(el); 

     } 

     @Override 

     public TextField clear() { 

       wrappedElement.clear(); 

       return this; 

     } 

     @Override 

     public TextField enterText(String text) { 

       char[] letters = text.toCharArray(); 

       for (char c: letters) {     

        wrappedElement.sendKeys(Character.toString(c)); 

        // because it is typing too fast... 

        try { 

          Thread.sleep(70); 

        } catch (InterruptedException e) { 

          e.printStackTrace(); 

        } 

       } 

       return this; 

     } 

     @Override 

     public ExpectedCondition<WebElement> isReady() { 

       return ExpectedConditions.elementToBeClickable(wrappedElement); 

     } 

} 

以下接口描述一個元件工廠:

public interface ElementFactory { 

     public <E extends Element> E create(Class<E> containerClass, WebElement wrappedElement); 

} 

的實施元件廠是:

public class DefaultElementFactory implements ElementFactory { 

     @Override 

     public <E extends Element> E create(final Class<E> elementClass, 

        final WebElement wrappedElement) { 

       E element; 

       try { 

        element = findImplementingClass(elementClass) 

            .getDeclaredConstructor(WebElement.class) 

            .newInstance(wrappedElement); 

       } 

       catch (InstantiationException e) { throw new RuntimeException(e);} 

       catch (IllegalAccessException e) { throw new RuntimeException(e);} 

       catch (IllegalArgumentException e) {throw new RuntimeException(e);} 

       catch (InvocationTargetException e) {throw new RuntimeException(e);} 

       catch (NoSuchMethodException e) { throw new RuntimeException(e);} 

       catch (SecurityException e) {throw new RuntimeException(e);} 

       return element; 

     } 



     private <E extends Element> Class<? extends E> findImplementingClass (final Class<E> elementClass) { 

       String pack = elementClass.getPackage().getName(); 

       String className = elementClass.getSimpleName(); 

       String interfaceClassName = pack+"."+className; 

       Properties impls = TestingProperties.getTestingProperties().getImplementations(); 

       if (impls == null) throw new RuntimeException("Implementations are not loaded"); 

       String implClassName = impls.getProperty(interfaceClassName); 

       if (implClassName == null) throw new RuntimeException("No implementation found for interface "+interfaceClassName); 

       try { 

        return (Class<? extends E>) Class.forName(implClassName); 

       } catch (ClassNotFoundException e) { 

        throw new RuntimeException("Unable to load class for "+implClassName,e); 

       } 

     } 

} 

工廠讀取屬性文件中使用所期望的實施的一種元素:

com.qamation.web.elements.Button = tests.application.elements.ApplicationButton 

com.qamation.web.elements.Link = tests.application.elements.ApplicationLink 

com.qamation.web.elements.TextField = tests.application.elements.ApplicationTextField 

com.qamation.web.elements.Label=tests.application.elements.ApplicationLabel 

元件工廠將要由FieldDecorator接口的實現一起使用。我將在下面討論。

此時元素的部分覆蓋已完成。這裏是總結:

每個元素由擴展Element接口的接口描述。

每個元素的實現擴展了AbstractElement類,並完成了isReady()以及其他所需的方法。

所需元素的實現應該在屬性文件中定義。

元素工廠將實例化一個元素,並通過裝飾器將它傳遞給PageFactory.initElement()。

起初看起來很複雜。

創建和使用簡單的元素來爲複雜的表單和頁面建模變得非常方便。

  1. 容器。

容器是一種將元素和其他容器放在一起以便爲複雜的Web表單和頁面建模的工具。

容器結構與元素相似,但它更簡單。

的容器由接口定義:

public interface Container { 

     public void init(WebElement wrappedElement); 

     public ExpectedCondition<Boolean> isReady(WebDriverWait wait); 

} 

的容器都有AbstractContainer基類:方法的參數是:

public abstract class AbstractContainer implements Container{ 

     private WebElement wrappedElement; 

     @Override 

     public void init(WebElement wrappedElement) { 

       this.wrappedElement = wrappedElement; 

     }  

     public abstract ExpectedCondition<Boolean> isReady(final WebDriverWait wait); 

} 

重要的是要注意容器的init()方法是很重要的WebElement接口的實例。

類似於一個元素,容器應該實現isReady()方法。差異在於返回類型:ExpectedCondition。

容器的「就緒」條件取決於包含在容器中的元素的組合。

使用布爾類型將幾個條件組合成一個是合乎邏輯的。

這裏是一個容器的一個示例:

public interface ContainerFactory { 

     public <C extends Container> C create(Class<C> wrappingClass, WebElement wrappedElement); 

} 

容器工廠的實現比元素的工廠簡單得多:

public class DefaultContainerFactory implements ContainerFactory { 

     @Override 

     public <C extends Container> C create(final Class<C> wrappingClass, 

        final WebElement wrappedElement) { 

       C container; 

       try { 

        container = wrappingClass.newInstance(); 

       } 

catch (InstantiationException e){throw new RuntimeException(e);} 

catch (IllegalAccessException e){throw new RuntimeException(e);} 

       container.init(wrappedElement); 

       return container; 

     } 

} 

由接口定義

public class LoginContainer extends AbstractContainer{ 

     @FindBy(id="Email") 

     private TextField username; 

     @FindBy(id="Passwd") 

     private TextField password; 

     @FindBy(id="signIn") 

     private Button submitButton; 

     public void login(final String username, final String password) { 

       this.username.clear().enterText(username); 

       this.password.clear().enterText(password); 

       this.submitButton.press(); 

     } 

     @Override 

     public ExpectedCondition<Boolean> isReady(final WebDriverWait wait) {  

       return new ExpectedCondition<Boolean>() { 

        @Override 

        public Boolean apply(final WebDriver driver) { 

          ExpectedCondition isUserNameFieldReady = username.isReady(); 

          ExpectedCondition isPasswordFieldReady = password.isReady(); 

          ExpectedCondition isSubmitButtonReady = submitButton.isReady(); 

          try { 

            wait.until(isUserNameFieldReady); 

            wait.until(isPasswordFieldReady); 

            wait.until(isSubmitButtonReady); 

            return new Boolean(true); 

          } 

          catch (TimeoutException ex) { 

            return new Boolean(false); 

          }       

        } 

       }; 

     } 

} 

集裝箱工廠以下是容器的簡短摘要:

容器用於將元素和其他容器合併爲一個單元。

容器的實現應該從AbstructContainer類擴展。它應該實現容器所需的isReady()和其他方法。

容器將被實例化並由容器工廠通過裝飾器傳遞給PageFactory.initElement()。

  • 頁是一個實例的webdriver和容器之間的橋樑。頁面可幫助將WebDriver與測試活動,測試數據供應和測試結果驗證分離。

    頁是通過一個接口,類似於容器中定義:

    public interface Page { 
    
         public void init(WebDriver driver); 
    
    } 
    

    容器和頁面之間的差異是在init():

    public abstract class AbstractPage implements Page { 
    
         protected WebDriver driver; 
    
         @Override 
    
         public void init(WebDriver driver) { 
    
           this.driver = driver; 
    
         } 
    
    } 
    

    頁的init方法採用一個WebDriver實例作爲參數。

    頁面實現應該擴展AbstractPage類。例如,一個簡單的Gmail頁面:

    public interface GMailPage extends Page { 
    
         public NewEmail startNewEmail(); 
    
    } 
    
    public class DefaultGMailPage extends AbstractPage implements GMailPage { 
    
         private LeftMenueContainer leftMenue; 
    
         public void init(final WebDriver driver) { 
    
           this.driver = driver; 
    
           leftMenue = new LeftMenueContainer();   
    
           PageFactory.initElements(new DefaultWebDecorator(driver), leftMenue); 
    
           WebDriverWait wait = new WebDriverWait(driver,TestingProperties.getTestingProperties().getTimeOutGeneral()); 
    
         ExpectedCondition<Boolean> isEmailFormReady = leftMenue.isReady(wait); 
    
         wait.until(isEmailFormReady); 
    
         } 
    
         @Override 
    
         public NewEmail startNewEmail() {  
    
           leftMenue.pressCompose(); 
    
           NewEmailWindowContainer newEmail = new NewEmailWindowContainer(); 
    
         PageFactory.initElements(new DefaultWebDecorator(driver), newEmail); 
    
         WebDriverWait wait = new WebDriverWait(driver,TestingProperties.getTestingProperties().getTimeOutGeneral()); 
    
         ExpectedCondition<Boolean> isNewEmailReady=newEmail.isReady(wait); 
    
           wait.until(isNewEmailReady); 
    
           return newEmail; 
    
         } 
    
    } 
    

    組件摘要:

    元素 - > AbstractElement - >元素的Emplementations - >元件廠

    容器 - > AbstractContainer - >集裝箱廠

    頁面 - >摘要頁面。

  • 裝飾
  • 的結構上面描述成爲活着時PageFactory.initElements()調用提供裝飾。

    基本實現已經存在 - DefaultFieldDecorator。讓我們使用它。

    public class DefaultWebDecorator extends DefaultFieldDecorator { 
    
         private ElementFactory elementFactory = new DefaultElementFactory(); 
    
         private ContainerFactory containerFactory = new DefaultContainerFactory(); 
    
    
    
         public DefaultWebDecorator(SearchContext context) { 
    
           super(new DefaultElementLocatorFactory(context)); 
    
         } 
    
         @Override 
    
         public Object decorate(ClassLoader classLoader, Field field) { 
    
           ElementLocator locator = factory.createLocator(field); 
    
           WebElement wrappedElement = proxyForLocator(classLoader, locator); 
    
           if (Container.class.isAssignableFrom(field.getType())) { 
    
            return decorateContainer(field, wrappedElement); 
    
           } 
    
           if (Element.class.isAssignableFrom(field.getType())) { 
    
            return decorateElement(field, wrappedElement); 
    
           } 
    
           return super.decorate(classLoader, field); 
    
         } 
    
         private Object decorateContainer(final Field field, final WebElement wrappedElement) { 
    
           Container container = containerFactory.create((Class<? extends Container>)field.getType(), wrappedElement); 
    
           PageFactory.initElements(new DefaultWebDecorator(wrappedElement), container);   
    
           return container; 
    
         } 
    
    
    
         private Object decorateElement(final Field field, final WebElement wrappedElement) { 
    
           Element element = elementFactory.create((Class<? extends Element>)field.getType(), wrappedElement); 
    
           return element; 
    
         } 
    
    } 
    

    請注意,在所有子元素和容器未初始化之前,decorateContainer()不會退出。

    現在,讓我們來看一個簡單的測試,按下Gmail頁面和檢查上撰寫按鈕,如果屏幕上出現一個新的電子郵件窗口:

    public class NewEmailTest { 
    
         private WebDriver driver; 
    
         @BeforeTest 
    
         public void setUp() { 
    
           driver = new FirefoxDriver(); 
    
           driver.manage().window().maximize(); 
    
         }  
    
         @AfterTest 
    
         public void tearDown() { 
    
           driver.close(); 
    
         }  
    
         @Test (dataProvider = "inputAndOutput", dataProviderClass = com.qamation.data.provider.TestDataProvider.class) 
    
         public void startNewEmailTest(DataBlock data) { 
    
           DefaultHomePage homePage = new DefaultHomePage(); 
    
           driver.manage().deleteAllCookies();  
    
           driver.get(data.getInput()[0]); 
    
           homePage.init(driver); 
    
           NewEmail newEmail = homePage.signIn().login(data.getInput()[1], data.getInput()[2]).startNewEmail();   
    
    
    
           for (String[] sa : data.getExpectedResults()) { 
    
            WebElement el = driver.findElement(By.xpath(sa[0])); 
    
            Assert.assertTrue(el.isDisplayed()); 
    
           } 
    
         } 
    
    } 
    

    當從Eclipse中運行測試,以下VM參數需要使用:

    -DpropertiesFile = testing.properties

    的淵源及QA和QA自動化幾個文章可以在這裏找到 http://qamation.blogspot.com