Java Client 1.6.0: @findBy annotations vs. driver.find

With Java Client > 1.6.0 (https://github.com/appium/java-client/pull/68), elements can be located with Annotations.

I personally love annotations but I am not 100% sure if they are suitable for my use case. I test Android and iOS native apps (clicking trough different views/screens and checking asserts). Currently, I search for the elements by

WebElement elem = driver.findElement(By.id(resourceID));

Now, I can also search for the elements with Annotations

public class MyClass {
	@AndroidFindBy(id="signUpButton")
	WebElement signUpButton;
      protected void someMethod() {
	PageFactory.initElements(new AppiumFieldDecorator(driver), this);
	signUpButton.click();
        .....
}

I assume that the Annotations are more useful when testing static pages/ views which do not change often? When clicking trough an app (like I do), I would always have to call the initElements method when switching between screens/views.

Or are those Annotations also meant to be used for usecases I described?

See also https://groups.google.com/forum/#!topic/appium-discuss/dXD2bUYW6ag

2 Likes

Let me explain…

It is not necessary to invoke

PageFactory.initElements(new AppiumFieldDecorator(driver), this);

each time you want to do something.

You can describe your screen like this

public class MyScreen {
@AndroidFindBy(id=“signUpButton”)
WebElement signUpButton;

 @AndroidFindBy(id="signUpButton")

WebElement oneAnotherElement;

//some more elements marked by @AndroidFindBy
....

public MyScreen(Webdriver driver, some parameters){
      ....
     new AppiumFieldDecorator(driver, this); //it doesn't perform search
     //it creates proxies using CGLIB library
}

  protected void someMethod() {
    signUpButton.click(); //search starts right now (!!!)
      //if element is found click will be performed
  }
    .....

}

So I think it is suitable with not only static pages/screens

If element is rendered not instantly , you can use this

PageFactory.initElements(new AppiumFieldDecorator(driver,
10, //default implicit waiting timeout for all strategies
TimeUnit.SECONDS),
this
);

3 Likes

If UI of Android or iOS versions of your app are similar, you can mark elements this way

@AndroidFindBy(some locator)
@iOSFindBy(some locator)
WebElement

It is one more useful option of new annotations

Thanks for explaining!

I already experimented with Annotations and think I will use them soon. Its great to have different annotations for iOS and Android. But, before changing my code finally, I would like to ask you some further questions.

  • I used “MobileElement” instead of “WebElement”. I did not see any troubles with it, but may I run into issues when using MobileElement (because of some cast)?
  • If I access a element which is not in the tree, I get a “NoSuchElementException” exception. From my point of view, it would also be a way to return a “null” object instead. Like this, I could use Java 8 “Optional” syntax to avoid Exceptions. What is your opinion about this?
  • What is the best workflow to check for non-present elements? E.g., I want to be sure that the user left the screen with “signUpButton”. Means, I don’t want the default implicit timeout to be used (PageFactory.initElements(new AppiumFieldDecorator(driver,
    10,TimeUnit.SECONDS), this);
    ) for this assert and just want to get sure that the element is not present anymore. Is there a good was or would you recommend using WebDriverWait instead?

Thanks a lot

3 Likes

The first question

Yeah! You can use MobileElement instead of WebElement.
It is possible to use WebElement, RemoteWebElement and MobileElement with AppumFieldDecorator.

You can look at my tests here

iOSPageObjectTest.java and AndroidPageObjectTest.java

Your second question.

If I access a element which is not in the tree, I get a “NoSuchElementException” exception. From my point of view, it would also be a way to return a “null” object instead. Like this, I could use Java 8 “Optional” syntax to avoid Exceptions. What is your opinion about this?

Throwing of NoSuchElementException is default behavior. It can’t be null because it is already proxy object.

But there are some ways.

For example, you can declare list of elements.

@AndroidFindBy(“some locator”)
List elements;

After you can check list size. This list has 0 size when there is nothing found.

Another way is to use ExpectedConditions and WebDriverWait.

And the third question

What is the best workflow to check for non-present elements? E.g., I want to be sure that the user left the screen with “signUpButton”. Means, I don’t want the default implicit timeout to be used (PageFactory.initElements(new AppiumFieldDecorator(driver,
10,TimeUnit.SECONDS), this);
) for this assert and just want to get sure that the element is not present anymore. Is there a good was or would you recommend using WebDriverWait instead?

I think I have answered. See above.

2 Likes

Hi there,
I did not want to open another topic, so I’m writing here the problem:

it seems that if I use the annotations it will not find the element, whilst using the classic driver.findElement(By.id("id from Localizable.strings")); the element will be found.

The strange issue happens on a registration screen for iOS where we have to test some edit text fields…However the buttons are found on the other screens …

This is what we have:

@iOSFindAll({@iOSFindBy(id = "register.firstName.placeholder"), @iOSFindBy(name = "First Name")})
private MobileElement etFirstName;

…and this will throw the following exception: org.openqa.selenium.NoSuchElementException: Cann't locate an element by this strategy: By.all({By.id: register.firstName.placeholder,By.name: First Name})
I am also invoking

PageFactory.initElements(new AppiumFieldDecorator(driver, 10, TimeUnit.SECONDS), this);

but the issue is the same.

when using

driver.findElement(By.id("register.firstName.placeholder")).sendKeys("blabla");

the editText field is found and the test continues.

Is there a rendering issue with the elements when using annotations?

It is interesting! I will give an answer as soon as possible.

Let me see the full stacktrace. You can send me a private message. Have you tried to set another time out (30 seconds, for example)?

@salmanchauhan, did u try with other locators? is it working fine with them??

@salmanchauhan Can you share information about how you solved this issue? I’m currently facing a similar problem

I think I found the answer for this problem (at least in my case).

I had an annotation with an empty string: @iOSFindBy(accessibility = "")

Commenting out the annotation solved the issue.

Error when using name in the @iOSFindBy in the java-client version 4.1.0. Is name is deprecated?

'name’is not part of the annotation io.appium.java_client.pagefactory.iOSFindBy

I have same confusion as @hatti when using annotation to find element in my project.
Currently it’s have same screen object as you mention above, however there’re some issues that i meet

  1. Some elements are existing but not display on screen, therefore I need to define a ListElement to know its exist or not before interacting with it. This solution lead us to duplicating elements also the method to handle that. With the flow i did in another project: Driver.FindElement -> element.isExisting -> element.isVisible -> click or do somethings else, this for me more flexible and easier when working around with elements
  2. Currently I don’t know how to pass the value to annotation when running. Im using Cucumber and there’re values from step that can be used as param for selector. example I have 3 labels: labelA, labelB, labelC and I want to pass to @FindBy(xpath = “//XCUIElementTypeStaticText[@name=‘VALUE’]”)