How to implement user types for @FindBy annotation?

I am trying to get from this:

@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; 

what

 @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; 

I previously created a class for each element, but of course nothing happens. How can I create my element class so that I can use it instead of WebElement?

Here's the InputBox code at the moment:

  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(); } } 
+4
source share
3 answers

Create a new FieldDecorator implementation.

When you use PageFactory, you are probably calling

  public static void initElements(ElementLocatorFactory factory, Object page) 

It will become

  public static void initElements(FieldDecorator decorator, Object page) 

Your FieldDecorator can behave similarly to DefaultFieldDecorator, with the exception of porting the proxy to your custom type.

See classes here [source]

+6
source

I found a very interesting post about how @FindBy works and how to use FieldDecorator tests in Selenium (WebDriver): http://habrahabr.ru/post/134462/ .

The author of the message is Roman Orazmagomedov (Roman Orazmagomedof).

Here I give more explanations on how to use FieldDecorator. I will also show an extended version of the original implementation with additional functionality that allows you to wait until the decorated field is ready using the ExpectedCondition interface.

Setup Tasks

Most Selenium page object template illustrations use the WebElement interface to define page fields:

 public class APageObject { @FindBy(id="fieldOne_id") WebElement fieldOne; @FindBy(xpath="fieldTwo_xpath") WebElement fieldTwo; <RESTO OF THE Page IMPLEMENTATION> } 

I would like to:

a) A page that will be a more general container with the ability to combine several forms together.

b) Use simple Java objects instead of the WebElement interface to declare fields on the page.

c) To have an easy way to determine if an element on a page is ready to use or not.

For instance:

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

Where APageForm is similar to APageObject, but with a slight difference - each field in the form is determined by the selected java class.

 public class APageForm { @FindBy(id="fieldOne_id") FieldOne fieldOne; @FindBy(xpath="fieldTwo_xpath") FieldTwo fieldTwo; <REST OF THE FORM IMPLEMENTATION> } 

There are two more important points:

a) This approach should use the Selenium ExpectedCondition;

b) This approach should help to separate the code between โ€œdata deliveryโ€ and โ€œdata validationโ€.

  • Element

    Open Interface Element {

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

    }

This interface should be extended to more complex elements such as button, link, shortcut, etc. For instance:

 public interface TextField extends Element { public TextField clear(); public TextField enterText(String text); public ExpectedCondition<WebElement> isReady(); } 

Each element must provide isReady () to avoid using Thread.sleep ().

Each element implementation must extend the AbstractElement class:

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

For instance:

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

The following interface describes the factory element:

 public interface ElementFactory { public <E extends Element> E create(Class<E> containerClass, WebElement wrappedElement); } 

factory element implementation:

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

factory reads the properties file to use the desired implementation for the element:

 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 

The factory element will be used by the implementation of the FieldDecorator interface. I will talk about this below.

At this point, the coverage of parts of the elements is completed. Here is a summary:

Each element is described by an interface that extends the Element interface.

Each element implementation extends the AbstractElement class and completes isReady () along with other necessary methods.

The implementation of the desired elements must be defined in the properties file.

The factory element will instantiate the element and pass it to PageFactory.initElement () through the decorator.

It seems complicated at first.

It is very convenient to create and use simple elements for modeling complex forms and pages.

  1. Container.

A container is a tool for storing elements and other containers for modeling complex web forms and pages.

The structure of the container is similar to an element, but simpler.

The container is defined by the interface:

 public interface Container { public void init(WebElement wrappedElement); public ExpectedCondition<Boolean> isReady(WebDriverWait wait); } 

The container has an AbstractContainer base class:

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

It is important to pay attention to the container init method: the method parameter is an instance of the WebElement interface.

Like an element, a container must implement the isReady () method. The difference is in the type of the return value: ExpectedCondition.

The Ready condition of the container depends on the combination of elements included in the container.

It is logical to combine several conditions into one using the Boolean type.

Here is an example 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); } } }; } } 

The factory container defined by the interface:

 public interface ContainerFactory { public <C extends Container> C create(Class<C> wrappingClass, WebElement wrappedElement); } 

container factory implementation is much simpler than factory elements:

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

Here is a quick overview for the container:

A container is used to combine elements and other containers into one block.

The container implementation must pass from the AbstructContainer class. It should implement isReady () and other methods required by the container.

The container will be created and transferred to the PageFactory.initElement () file by the factory container through the decorator.

  1. Page

The page is the bridge between the WebDriver instance and the containers. The page helps separate WebDriver from testing, validating test data, and validating test results.

The page is defined by an interface similar to the container:

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

The difference between the container and the page is in init ():

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

The init method of the page takes an instance of WebDriver as a parameter.

The implementation of the page should extend the AbstractPage class. For example, a simple gmail page:

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

Component Summary:

Element โ†’ Absolute Element โ†’ Element Emulation โ†’ Factory Element

Container โ†’ Abstract Container โ†’ Factory Container

Page โ†’ AbstractPage.

  1. decorator

The constructs described above become alive when PageFactory.initElements () calls the provided decorator.

Major implementations already exist - DefaultFieldDecorator. Lets use it.

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

Note that decorateContainer () does not end until all auxiliary elements and containers are initialized.

Now let's look at a simple test that clicks the Create button on the gmail page and checks to see if a new email window appears on the screen:

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

When starting a test from Eclipse, you must use the following VM arguments:

-DpropertiesFile = testing.properties

Sources and some more articles about QA and QA Automation can be found here http://qamation.blogspot.com

+6
source

First assumption: you thought about a more convenient naming convention. In my class, the buttons look like this:

 private WebElement loginButton; 

In my selenium tests, I found out that the best approach is to have a class for each page, for example:

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

And then the test looks like this:

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

but on condition that this does not work for you, there are my two guseeses:

First, you can go from WebElement:

 public class Button extends WebElement{ 

but you may have to implement all of the public WebElement methods, even if you don't use them.

Then, as a second assumption, you can send the driver and find the path to the constructor

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

and the call will be:

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

By the way, my assumption is that you are using the WebDriver approach

EDIT I found out that WebElement is an interface. So you can do something like this:

 public class WebButton implements WebElement{ 

but you will have to implement all the abstract methods of the WebElement interface.

Anyway, when I did this, it allowed me to make this annotation in my other class:

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

but I have never used this approach and cannot guarantee that he will do something ...

If you're interested, I applied an example of my implementation here:

http://pastebin.com/STr15UQd

0
source

Source: https://habr.com/ru/post/1398770/


All Articles