Wait for element to be present or clickable

I have a problem with waiting for elements, on app which I try to test. Sometimes Appium doesn’t locate element.

Here is my BaseTests class
package Base;

import helpers.TestValues;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;

public class BaseTests {
    protected AndroidDriver<AndroidElement> driver;

    @BeforeMethod
    public void setup () throws MalformedURLException {
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability("deviceName", TestValues.devicesInfo.Honor10);
        caps.setCapability("platformName", TestValues.devicesInfo.PlatformName);
        caps.setCapability("platformVersion", TestValues.devicesInfo.Android810Version);
        caps.setCapability("skipUnlock", TestValues.devicesInfo.SkipUnlock);
        caps.setCapability("appPackage", TestValues.devicesInfo.AppPackageWageName);
        caps.setCapability("appActivity", TestValues.devicesInfo.AppPackageWageActivity);
        caps.setCapability("noReset","false");
        driver = new AndroidDriver<AndroidElement>(new URL("http://127.0.0.1:4723/wd/hub"),caps);
        driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
       }

    @AfterMethod
    public void teardown(){
        driver.quit();
    }
}

And here is MainPage class with page objects
package PageObjects;

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import org.openqa.selenium.support.PageFactory;



public class MainPage {
    private AndroidDriver<AndroidElement> driver;

    public MainPage(AndroidDriver<AndroidElement> driver) {
        this.driver = driver;
        PageFactory.initElements(new AppiumFieldDecorator(driver) ,this);
    }
    @AndroidFindBy(id = "add_task_button")
    private AndroidElement AddTaskButton;

    @AndroidFindBy(id = "com.android.packageinstaller:id/permission_allow_button")
    private AndroidElement PermissionAllowButton;

    public void ClickOnAllowButton(){
        PermissionAllowButton.click();
    }
    public boolean IsAddTaskVisible(){
        return AddTaskButton.isDisplayed();
    }
    public void ClickOnAddTaskButton() {

        AddTaskButton.click();
    }

}

And the test
@Test
public void PostTask (){
_landingPage.clickOnLoginButton();
_loginPage.TypeEmail(TestValues.Credentials.CorrectEmail);
_loginPage.TypePassword(TestValues.Credentials.CorrectPassword);
_loginPage.ClickOnLoginBUtton();
_mainPage.ClickOnAllowButton();
//here is loading spinner because app fetch data from API
_mainPage.ClickOnAddTaskButton();
_mainPage.ClickOnAllowButton();
_postTaskPage.SendPhoto();
}

In most cases my test fails on clicking add task button, even if loading spinner disappears Appium tries to locate it, because I see in console
[AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got data from client: {“cmd”:“action”,“action”:“find”,“params”:{“strategy”:“id”,“selector”:“add_task_button”,“context”:“”,“multiple”:false}}

    [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command of type ACTION

    [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command action: find

    [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Finding 'add_task_button' using 'ID' with the contextId: '' multiple: false

    [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Using: UiSelector[INSTANCE=0, RESOURCE_ID=io.wageapp.android:id/add_task_button]

    [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Using: UiSelector[INSTANCE=0, RESOURCE_ID=android:id/add_task_button]

    [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Using: UiSelector[INSTANCE=0, RESOURCE_ID=add_task_button]

    [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Using: UiSelector[DESCRIPTION=add_task_button, INSTANCE=0]

    [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Failed to locate element. Clearing Accessibility cache and retrying.

    [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Finding 'add_task_button' using 'ID' with the contextId: '' multiple: false

But mostly it fails with error

org.openqa.selenium.NoSuchElementException: Can’t locate an element by this strategy: By.chained({By.id: add_task_button})

But id is correct (and sometimes it locate it successfully)

What am I doing wrong? And how should I handle “waits” properly to make my tests resistant to situation like this.

Thanks in advance

I think this article by Jonathan Lipps gives a good discussion and solution(s) for the issue you are seeing. Read this and see if that helps you to harden your test framework:

I tried Explicit and Implicit waits, but these doesn’t resolve my problem with random “no element” error. I ran test and it found all element, then I ran it again and it didn’t find :confused:

And as I checked, this happens only when navigation bar is visible,when I move to other activity without navigation bar everything is ok. Also during login process I don’t have any problems.