Maintaining Android Mobile UI test codes, which are already structured, is relatively easier than starting them from scratch.
Building/adapting an architecture, defining libraries and their compatibilities ends up consuming precious time when we need to build end-to-end functional tests (E2E).
To get around this issue, we developed an Open-source library in order to facilitate the integration of native UI tests for Android in Kotlin, which aims to reduce the time and complexity of initial configuration of these test modules.
Our goal is to provide resources so you can start a test package quickly, while we provide you with settings and features you won't waste time worrying about!
Note that the library is experimental, and the API is subject to change.
The library is published to Maven Central.
To use the Kranberry library, follow the steps:
Add the Maven Central repository in the root/build.gradle file if it is not already there:
maven {
url "https://plugins.gradle.org/m2/"
}
- Add the Kranberry dependency to your
module/build.gradle
file inside the package that will be tested (not the build.gradle file in the project root):
// Kranberry
androidTestImplementation 'io.github.kranberry-io:kranberry:1.0.1-beta'
-
Add a
kranberry.properties.json
file in the pathmodule/src/androidTest/assets
. If the folder does not exist, you must create it. π‘ See the template -
Add an
AndroidManifest.xml
file in the pathmodule/src/androidTest
. If the folder does not exist, you must create it. π‘ See the template -
Add an
App.kt
class in the pathmodule/src/androidTest/java/feature
. If the folder does not exist, you must create it. This step will serve to implement the method that will open your app. π‘ See the template -
Add a
Home.kt
class in the pathmodule/src/androidTest/java/feature
. If the folder does not exist, you must create it. This step will serve to implement navigation steps and assertions. π‘ See the template -
Add a test using the pre-implemented App and Home features. If the folder does not exist, you must create it. This step will serve to implement navigation steps and assertions. π‘ See the template
-
Add the
/kranberry-outputs/
information to yourroot/gitignore
file. This will prevent you from publishing test outputs in your repository.
# Output Files Kranberry Library
/kranberry-outputs/
/app/kranberry-outputs/
- If you want to run your tests in a customized way, you can include a Makefile , modifying the execution tasks with desired parameters. π‘ See the template
π₯³ π VoilΓ ! Now you can run your first test from the terminal command line by using make run-kranberry
:
Note that there is a previously opened emulator to run the tests.
π This feature is under construction! But you can use the current version which doesn't have the output printing in the terminal console yet..
- Now you can also use the gradle plugin to run the tests from the
./gradlew runKranberryTests
command. To do this include the settings in thebuild.gradle
andmodule:build.gradle
files :
root/build.gradle
buildscript
ext.kranberry_version = '1.0.1-beta'
dependencies
classpath "io.github.kranberry-io:runtests:$kranberry_version"
module/build.gradle
apply plugin: "io.github.kranberry-io.runtests"
kranberryTests
kranberryTests {
packageName = "your.app.package"
packageTests = "your.app.package.test"
applicationId = "your.app.package"
apkOutputPath = "build/outputs/apk/debug/app-debug.apk"
androidTestApkOutputPath = "build/outputs/apk/androidTest/debug/app-debug-androidTest.apk"
testsRunner = "androidx.test.runner.AndroidJUnitRunner"
}
π₯³ π VoilΓ ! Now you can run your first test from the terminal command line by using ./gradlew runKranberryTests
This is the configuration file used by the library to configure the values that will be used to set the test environment.
The file must be located in the src/androidTest/assets
directory with name kranberry.properties.json
.
Below you will find more details about the parameters that can be used:
Parameter | Description | Type | Obligatory | Default Value |
---|---|---|---|---|
app_packages | Packages of the applications that will be tested. The first package in the list will be referenced by the Kranberry constant APP_PACKAGE . The others will be used for permission management. |
List | Yes | io.kranberry.sample |
clear_application _data_before_testing | Defines whether the application data defined in the app_packages parameter list will be cleared before running the tests. |
Boolean | No | FALSE |
default_timeout | Default implicit timeout for interaction with UI elements in milliseconds. | Long | No | 60000 |
disable_animations | Defines whether animations will be disabled during instrumentation tests. The goal is to increase test performance. | Boolean | No | TRUE |
max_search_swipes | Sets the maximum scroll down/up will be performed when fetching an element using the methods of the io.kranberry.ui.Swipe class. |
Int | No | |
page_load_timeout | Defines the maximum implicit wait time for the element set in the progressbar_class property. This is used in Kranberry's waitForPageLoad() method. |
Long | No | 30000 |
permissions_granted _to_device | Defines the list of permissions that will be granted to app packages set in the app_packages property. |
List | No | ACCESS_FINE_LOCATION |
print_colored_logs | Defines if the terminal logs will be printed colored. | Boolean | No | TRUE |
progressbar_class | Defines the progress bar class that will be implicitly expected. This is used in Kranberry's waitForPageLoad() method. |
List | No | android.widget .ProgressBar |
skip_chrome_welcome _screen | Defines whether the application data defined in the app_packages parameter list will be cleared before running the tests. |
Boolean | No | TRUE |
swipe_down_params | Defines the list of parameters that will be used in method swipeDown(). These being in order: SWIPE_DOWN_START_X, SWIPE_DOWN_START_Y, SWIPE_DOWN_END_X, SWIPE_DOWN_END_Y, SWIPE_DOWN_STEPS |
List | No | |
swipe_up_params | Defines the list of parameters that will be used in method swipeUp(). These being in order: SWIPE_UP_START_X, SWIPE_UP_START_Y, SWIPE_UP_END_X, SWIPE_UP_END_Y, SWIPE_UP_STEPS |
List | No |
{
"default_timeout": 60000,
"clear_application_data_before_testing": true,
"skip_chrome_welcome_screen": true,
"page_load_timeout": 30000,
"disable_animations": true,
"app_packages": [
"com.android.chrome"
],
"permissions_granted_to_device": [
"ACCESS_FINE_LOCATION",
"WRITE_EXTERNAL_STORAGE",
"READ_EXTERNAL_STORAGE",
"READ_CONTACTS",
"CAMERA"
],
"progressbar_class": [
"android.widget.ProgressBar"
]
}
See the open issues for a list of proposed features (and known issues).
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
module/src/androidTest/assets/kranberry.properties.json
{
"default_timeout": 60000,
"clear_application_data_before_testing": false,
"skip_chrome_welcome_screen": true,
"page_load_timeout": 30000,
"disable_animations": true,
"app_packages": [
"your.app.package"
],
"permissions_granted_to_device": [
"ACCESS_FINE_LOCATION",
"WRITE_EXTERNAL_STORAGE",
"READ_EXTERNAL_STORAGE",
"READ_CONTACTS",
"CAMERA"
],
"progressbar_class": [
"android.widget.ProgressBar"
],
"print_colored_logs": true,
"test-class_prefix": "your.app.package.test"
}
module/src/androidTest/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="your.app.package.test">
<uses-sdk
android:minSdkVersion="24"
android:targetSdkVersion="30" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</manifest>
module/src/androidTest/java/feature/App.kt
package feature
import androidx.test.uiautomator.UiDevice
import io.kranberry.environment.DeviceHandler.APP_PACKAGE
import io.kranberry.environment.DeviceHandler.start
import io.kranberry.outputs.ScreenshotHandler.takeScreenshot
import io.kranberry.ui.BaseUi
class App(device: UiDevice) : BaseUi(device) {
fun open(): Home {
start(APP_PACKAGE)
takeScreenshot()
return Home(device)
}
}
module/src/androidTest/java/feature/Home.kt
package feature
import androidx.test.uiautomator.UiDevice
import io.kranberry.outputs.ScreenshotHandler.takeScreenshot
import io.kranberry.ui.BaseUi
import io.kranberry.ui.elementIsPresentByTextContains
class Home(device: UiDevice) : BaseUi(device) {
fun shouldSeeFlowerList(): Home {
assert(elementIsPresentByTextContains("Flowers"))
takeScreenshot()
return this
}
}
module/src/androidTest/java/your/app/package/YourTestJourney.kt
package your.app.package
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import feature.App
import io.kranberry.KranberryRules
import io.kranberry.environment.TestHandler.device
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = 24)
class YourTestJourney {
@Rule
@JvmField
val testRule = KranberryRules()
@Test
fun openApp(){
App(device)
.open()
.shouldSeeFlowerList()
}
}
}
root/Makefile
### BUILD ####
buildDebug:
./gradlew assembleDebug
buildAndroidTestDebug:
./gradlew assembleDebugAndroidTest
### INSTALL ###
installDebug:
adb install app/build/outputs/apk/debug/app-debug.apk
installAndroidTestDebug:
adb install app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk
buildInstallDebug: buildDebug installDebug
### TEST ###
testDebug:
./gradlew testDebug
testFileDebug:
./gradlew testDebug --tests $(file)
androidTestDebug:
./gradlew connectedDebugAndroidTest
androidTestDebugModule:
./gradlew :$(module):connectedDebugAndroidTest
androidTestFileDebugModule:
./gradlew :$(module):connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=$(file)
clear-logcat-logs:
@adb shell logcat -b all -c
copy-tests-outputs:
@rm -rf kranberry-outputs; \
mkdir kranberry-outputs; \
adb pull /storage/emulated/0/Android/media/your.app.package kranberry-outputs; \
### RUN UI TESTS ###
# before run tests make sure to call: buildDebug buildAndroidTestDebug installDebug installAndroidTestDebug
test: buildDebug buildAndroidTestDebug installDebug installAndroidTestDebug clear-logcat-logs
@adb logcat *:S KRANBERRY_LOG:V & LOGCAT_PID=$$!; \
TERM=dumb adb shell am instrument -w your.app.package.test/androidx.test.runner.AndroidJUnitRunner ; \
RESULT=$$?; \
if [ -n "$$LOGCAT_PID" ]; then kill $$LOGCAT_PID; fi; \
exit $$RESULT
run-kranberry:
@make test && make copy-tests-outputs
Distributed under the Apache Version 2.0 License. See LICENSE
for more information.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this software except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.