2012-02-28 53 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