Mendix Mobile Test Automation with Appium — Part I
How can we ensure the quality of our apps without tests? And how can we be efficient in Quality Assurance without automated tests? In my opinion, automated tests are a fundamental prerequisite for any project, mobile or web. This article intends to show how can you achieve this using Appium with a Mendix Native Mobile Application.
As a mobile project progresses, ensuring that all requirements remain met becomes increasingly complex. Automated tests help to minimize the associated costs while helping to keep the project challenges under control. But how can we do automated tests in a low-code platform like Mendix? Well, for a Web Application, you can use tools like Selenium or Cypress. And for Native Applications, you can use Appium. The latter is the subject of this article, using a Mendix Native Mobile Application to be automated.
Scenario
Let’s consider you have a Mendix Native Mobile Application Project and plan to ensure and improve the quality of your final application by using automated tests. There are several types of tests, and explaining all of them is beyond the scope of this article. Here we’ll focus only on end-to-end tests using Appium.
Our app is very simple: it takes two numbers and displays the results of their sum.
Procedure
- [1] Connect your Android physical device to your computer via USB. If you prefer, can use a screen mirror software like ScrCpy. In addition, enable USB debugging on your device. It’s also possible to use an Android Emulator.
– Since Android Studio Flamingo, you can also mirror your device from the IDE! - [2] [Optional] Assuming you already have a Mendix Native Mobile Project created, I recommend building a Custom Developer App specific to your project. It’s possible to use Make it Native 9 for this, but I personally always prefer to create my custom developer app launcher.
- [3] Get the Application Package Identifier: if you are using a Custom Developer app, this identifier is the one defined in the NBUI tool, followed by
.developerapp
. The complete value would be likemyapp.appiumapp.developerapp
. If you are using Make it Native 9, the value iscom.mendix.developerapp.mx9
.
- [4] Get the Application Activity Identifier: for the Custom Developer App is
com.mendix.nativetemplate.MainActivity
. For Make it Native 9, iscom.mendix.developerapp.MainActivity
. - [5] [Hint] You can get both values at once by running the command line below on Windows Power Shell (You must have Android SDK Tools installed):
adb shell dumpsys window windows | findStr 'imeLayeringTarget'
- For Make it Native 9, the result is:
imeLayeringTarget in display# 0 Window{b871f63 u0 com.mendix.developerapp.mx9/com.mendix.developerapp.MainActivity}
- Check the pattern
appPackage/appActivity
at the end of the command line output:
- [6] Install Appium by running the command line below
npm i -g appium@next
- [7] Install Appium UI Automator 2 driver
appium driver install uiautomator2
- [8] Install Appium Inspector. It’s possible to use the Appium web app, but for the purposes of this article, and also for security reasons, I prefer using the desktop version
- [9] Start Appium Server
appium
- [10] Execute Appium Inspector and configure the Desired Capabilities as shown in the image (you can copy&paste the snippet below):
{
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"appium:appPackage": "myapp.appiumapp.developerapp",
"appium:appActivity": "com.mendix.nativetemplate.MainActivity",
"appium:noReset": true
}
- [11] Click the Start Session button. This will launch the application “inside” Appium Inspector, allowing you to inspect the widget structure tree and interact with your app UI:
- [12] In the Appium Inspector UI, we can record the actions performed in your app, so that is possible to play such actions again afterward. To do that, click on the eye icon
- [13] Using the commands available after you select an element, proceed with the usual actions you would do if having the device in your hands. Many languages are available for recording the commands, but I prefer Python. For example, the first interaction to be recorded would be: i) Type the URL to
http://192.168.0.71:8080/
in the textEdit — using the Send Keys command — and then ii) Hit the LAUNCH button — using the Tap command.
- [14] At this point, Appium Inspector should be showing the application’s main page with our simplified calculator. We can continue recording the actions: iii) Select the first textBox, tap on it, clear its contents, and type
5
. iv) Select the second textBox, tap on it, clear its contents, and type2
. v) Tap on any area outside the widgets, to hide the keyboard. vi) Finally, select the Calculate button and tap on it. All commands will be recorded, as shown below:
- [15] The full code can be copied to the clipboard. Click Show/Hide boilerplate code and then click Copy code to clipboard.
# This sample code uses the Appium python client v2
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
# For W3C actions
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput
caps = {}
caps["platformName"] = "Android"
caps["appium:automationName"] = "UiAutomator2"
caps["appium:appPackage"] = "myapp.appiumapp.developerapp"
caps["appium:appActivity"] = "com.mendix.nativetemplate.MainActivity"
caps["appium:noReset"] = True
caps["appium:ensureWebviewsHavePages"] = True
caps["appium:nativeWebScreenshot"] = True
caps["appium:newCommandTimeout"] = 3600
caps["appium:connectHardwareKeyboard"] = True
driver = webdriver.Remote("http://127.0.0.1:4723", caps)
el1 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="appUrl")
el1.send_keys("http://192.168.0.71:8080/")
el2 = driver.find_element(by=AppiumBy.ID, value="myapp.appiumapp.developerapp:id/launch_app_button")
el2.click()
el3 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="Value1")
el3.click()
el3.clear()
el3.send_keys("5")
el4 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="Value2")
el4.click()
el4.clear()
el4.send_keys("2")
el5 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="Result, Result")
el5.click()
el6 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="Calculate, ")
el6.click()
driver.quit()
- [16] Since we chose to use Python a scripting language, you must first install the Appium dependencies
pip install Appium-Python-Client
- [17] Finally, after copying the code into a file called
script.py
, we can run the command and cross our fingers to see if it runs successfully:
python .\script.py
- [18] And to our disappointment, the result is an ERROR!
...
Traceback (most recent call last):
File "D:\coe\projetos\appium\AppiumApp\tests\script.py", line 32, in <module>
el3 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="Value1")
...
selenium.common.exceptions.NoSuchElementException: Message: An element could not be located on the page using the given search parameters.
Stacktrace:
NoSuchElementError: An element could not be located on the page using the given search parameters.
...
Going to Fast …
The reason for the above error is that the Custom Developer App has not yet had time to load the package with Mendix Code. We must instruct the script to wait a little while the code is still loading. Add the code driver.implicitly_wait
as below:
...
driver = webdriver.Remote("http://127.0.0.1:4723", caps)
driver.implicitly_wait(5000)
...
It’s like magic!
After this small correction in the generated code, finally, we can run the code without errors:
Conclusions & Next Steps
As you can see, automation is a valuable tool during code development. In this simple example, this is not so evident, but imagine a complex flow with many steps to reach a given page … This script can be useful either for the developer or for the testing guy reaching that page.
But the script generated by the Appium Inspector is not yet the code we can use for testing. It is just a snippet we can use to write the definitive code, with the AAA Test Pattern (Arrange, Act & Assert).
This will be the subject of Part II of this article, so stay tuned!
Let’s go Making (with quality)!