I'm writing mobile test using Appium UIAutomator2 Java JUnit5. The point is when I'm calling the code from the test everything works fine and all elements can be located. When I'm using the same code in a PO model function, one element cannot be located and the test fails. I have no idea why.
Working solution:
@Test
public void loginTest () throws InterruptedException {
WelcomeScreen welcomeScreen = new WelcomeScreen(driver);
welcomeScreen.clickLoginButton();
LoginScreen loginScreen = new LoginScreen(driver);
loginScreen.typeEmail("[email protected]");
loginScreen.clickNextButton();
PasswordScreen passwordScreen = new PasswordScreen(driver);
passwordScreen.typePassword("Password12345");
passwordScreen.clickLoginButton();
}
Failing solution (upd below, works with one condition):
@Test
public void loginTest () throws InterruptedException {
PasswordScreen passwordScreen = new PasswordScreen(driver);
passwordScreen.login("[email protected]", "Password12345");
}
UPD (Aug 20, 2022): The order of the code execution is first to find all the declared elements, only then the tests can be executed and class objects are created. Originally I have helper wrapper function to allocate elements, that can process exceptions (I haven't paste it here to save some space in the beginning, will add it now below). Using this function it becomes more interesting: Running the test it still not able to find passwordInput element, but not fail the test, since the exception is processed. This allows to start the test. The fun part is that when the class object PasswordScreen passwordScreen is created, no problem now happen and the test can be passed.
Helper function:
public static Boolean xpathElementIsPresent(String text) {
Logger logger = Logger.getLogger("LOG:");
WebDriverWait wait = new WebDriverWait (driver, Duration.ofSeconds(5));
try {
wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@text='%s']".formatted(text))));
return true;
} catch (Exception e) {
logger.log(Level.WARNING,"No element found by xpath: %s".formatted(text));
return false;
}
public static WebElement findByXpath(String text) {
WebElement element;
if (xpathElementIsPresent(text)){
element = driver.findElement(By.xpath("//*[@text='%s']".formatted(text)));
return element;
}
return null;
}
PasswordScreen class:
public class PasswordScreen extends BaseDriver {
public PasswordScreen (AndroidDriver driver) {
this.driver = driver;
}
/* private WebElement passwordInput = driver.findElement(By.xpath("//*[@text='%s']".formatted("Password")));
Using this code the original problem appears with the logs and exception provided below.
The WebElement can't be located in case of calling from passwordScreen.login(),
But works calling it from the test. login() function call from the class with tests also works */
private WebElement passwordInput = findByXpath("Password");
/* Finding element like this processes the NoSuchElementError and the test can be executed after that.
The main question now is why? */
private WebElement loginButton = findByAccessibilityId("Log in");
@Step
public void typePassword(String password) {
WebElement passwordInput = this.passwordInput;
typeText(passwordInput, password);
}
@Step
public void clickLoginButton() {
WebElement loginButton = this.loginButton;
loginButton.click();
}
@Step
public void login(String email, String password) {
WelcomeScreen welcomeScreen = new WelcomeScreen(driver);
welcomeScreen.clickLoginButton();
LoginScreen loginScreen = new LoginScreen(driver);
loginScreen.typeEmail(email);
loginScreen.clickNextButton();
PasswordScreen passwordScreen = new PasswordScreen(driver);
passwordScreen.typePassword(password);
passwordScreen.clickLoginButton();
} // ^^^ Exact the same code that was used in working solution
}
The point is that LoginScreen class has absolutely the same logic and structure and WebElement emailInput there has no errors using the same finder.
Logs:
[HTTP] {"using":"xpath","value":"//*[@text='Password']"}
[debug] [AndroidUiautomator2Driver@bda7 (7345ce7a)] Calling AppiumDriver.findElement() with args: ["xpath","//*[@text='Password']","7345ce7a-7b53-4fe3-a3f2-79ca9f4f3073"]
[debug] [AndroidUiautomator2Driver@bda7 (7345ce7a)] Valid locator strategies for this request: xpath, id, class name, accessibility id, css selector, -android uiautomator
[debug] [AndroidUiautomator2Driver@bda7 (7345ce7a)] Waiting up to 10000 ms for condition
[debug] [AndroidUiautomator2Driver@bda7 (7345ce7a)] Matched '/element' to command name 'findElement'
[debug] [AndroidUiautomator2Driver@bda7 (7345ce7a)] Proxying [POST /element] to [POST http://127.0.0.1:8200/session/c63da5b5-f741-4b68-87ca-2bd161e3b3af/element] with body: {"strategy":"xpath","selector":"//*[@text='Password']","context":"","multiple":false}
[AndroidUiautomator2Driver@bda7 (7345ce7a)] Got response with status 404: {"sessionId":"c63da5b5-f741-4b68-87ca-2bd161e3b3af","value":{"error":"no such element","message":"An element could not be located on the page using the given search parameters","stacktrace":"io.appium.uiautomator2.common.exceptions.ElementNotFoundException: An element could not be located on the page using the given search parameters\n\tat io.appium.uiautomator2.handler.FindElement.findElement(FindElement.java:90)\n\tat io.appium.uiautomator2.handler.FindElement.safeHandle(FindElement.java:67)\n\tat io.appium.uiautomator2.handler.request.SafeRequestHandler.handle(SafeRequestHandler.java:59)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleRequest(AppiumServlet.java:267)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleHttpRequest(AppiumServlet.java:261)\n\tat io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:68)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)\n\tat io.netty.channel.AbstractChannelHandlerCont...
[debug] [W3C] Matched W3C error code 'no such element' to NoSuchElementError
Exception:
An element could not be located on the page using the given search parameters.
For documentation on this error, please visit: https://selenium.dev/exceptions/#no_such_element
Build info: version: '4.4.0', revision: 'e5c75ed026a'
System info: host: 'ip-192-168-0-13.eu-west-1.compute.internal', ip: '192.168.0.13', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '12.5', java.version: '17.0.4.1'
Driver info: io.appium.java_client.android.AndroidDriver
Command: [7345ce7a-7b53-4fe3-a3f2-79ca9f4f3073, findElement {using=xpath, value=//*[@text='Password']}]
Capabilities {appium:app: /Users/.../IdeaPro..., appium:appPackage: com.org.wallet.cordova, appium:automationName: UiAutomator2, appium:databaseEnabled: false, appium:desired: {app: /Users/.../IdeaPro..., automationName: UiAutomator2, deviceName: emulator-5554, noReset: false, platformName: android}, appium:deviceApiLevel: 30, appium:deviceManufacturer: Google, appium:deviceModel: sdk_gphone_x86, appium:deviceName: emulator-5554, appium:deviceScreenDensity: 440, appium:deviceScreenSize: 1080x2160, appium:deviceUDID: emulator-5554, appium:javascriptEnabled: true, appium:locationContextEnabled: false, appium:networkConnectionEnabled: true, appium:noReset: false, appium:pixelRatio: 2.75, appium:platformVersion: 11, appium:statBarHeight: 66, appium:takesScreenshot: true, appium:viewportRect: {height: 1962, left: 0, top: 66, width: 1080}, appium:warnings: {}, appium:webStorageEnabled: false, platformName: ANDROID}
Session ID: 7345ce7a-7b53-4fe3-a3f2-79ca9f4f3073
org.openqa.selenium.NoSuchElementException: An element could not be located on the page using the given search parameters.
For documentation on this error, please visit: https://selenium.dev/exceptions/#no_such_element
Build info: version: '4.4.0', revision: 'e5c75ed026a'
System info: host: 'ip-192-168-0-13.eu-west-1.compute.internal', ip: '192.168.0.13', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '12.5', java.version: '17.0.4.1'
Driver info: io.appium.java_client.android.AndroidDriver
Command: [7345ce7a-7b53-4fe3-a3f2-79ca9f4f3073, findElement {using=xpath, value=//*[@text='Password']}]
Capabilities {appium:app: /Users/.../IdeaPro..., appium:appPackage: com.org.wallet.cordova, appium:automationName: UiAutomator2, appium:databaseEnabled: false, appium:desired: {app: /Users/.../IdeaPro..., automationName: UiAutomator2, deviceName: emulator-5554, noReset: false, platformName: android}, appium:deviceApiLevel: 30, appium:deviceManufacturer: Google, appium:deviceModel: sdk_gphone_x86, appium:deviceName: emulator-5554, appium:deviceScreenDensity: 440, appium:deviceScreenSize: 1080x2160, appium:deviceUDID: emulator-5554, appium:javascriptEnabled: true, appium:locationContextEnabled: false, appium:networkConnectionEnabled: true, appium:noReset: false, appium:pixelRatio: 2.75, appium:platformVersion: 11, appium:statBarHeight: 66, appium:takesScreenshot: true, appium:viewportRect: {height: 1962, left: 0, top: 66, width: 1080}, appium:warnings: {}, appium:webStorageEnabled: false, platformName: ANDROID}
Session ID: 7345ce7a-7b53-4fe3-a3f2-79ca9f4f3073
at [email protected]/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at [email protected]/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
at [email protected]/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at [email protected]/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at [email protected]/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at app//org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec.createException(W3CHttpResponseCodec.java:200)
at app//org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:133)
at app//org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:53)
at app//org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:184)
at app//io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumCommandExecutor.java:180)
at app//org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:547)
at app//org.openqa.selenium.remote.ElementLocation$ElementFinder$2.findElement(ElementLocation.java:162)
at app//org.openqa.selenium.remote.ElementLocation.findElement(ElementLocation.java:60)
at app//org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:365)
at app//org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:357)
at app//org.example.po.PasswordScreen.<init>(PasswordScreen.java:18)
at app//org.example.HealthCheckTest.basicTest(HealthCheckTest.java:31)
at [email protected]/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at [email protected]/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at [email protected]/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at [email protected]/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at app//org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at app//org.junit.jupiter.api.extension.InvocationInterceptor.interceptTestMethod(InvocationInterceptor.java:118)
at app//org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at app//org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at app//org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at app//org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at app//org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at app//org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at app//org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at [email protected]/java.util.ArrayList.forEach(ArrayList.java:1511)
at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at [email protected]/java.util.ArrayList.forEach(ArrayList.java:1511)
at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at app//org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at app//org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
at [email protected]/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at [email protected]/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at [email protected]/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at [email protected]/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
CodePudding user response:
This may work. You are trying to pass driver which is not accessible to your functions apart from the constructor. Then you are trying to create an object of the class in its definition itself.
@Step
public void login(String email, String password) {
WelcomeScreen welcomeScreen = new WelcomeScreen(this.driver);
welcomeScreen.clickLoginButton();
LoginScreen loginScreen = new LoginScreen(this.driver);
loginScreen.typeEmail(email);
loginScreen.clickNextButton();
this.typePassword(password);
this.clickLoginButton();
}
CodePudding user response:
So all my problems are caused because I'm calling the login() function from the PasswordScreen class. At that moment all the variables seems cannot be yet declared, because the screen is not open in the app. The easiest solution was to move login() function to separate class and to call it from there. This solution allows to declare the variables in the right order while appropriate screen is open