JUnit Automated App Testing
See our JUnit example repository for a simple example on how to run JUnit mobile app tests.
Installation
First, make sure you install the necessary dependencies to run a test. For this example, we'll be using JUnit 5 and the Appium Java Client.
Add the following dependencies to your pom.xml:
<dependencies>
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- Appium Java Client -->
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>10.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Note: Appium Java Client 10.x requires Java 11 or higher.
JUnit Example
The example below shows a complete JUnit 5 test that connects to the TestingBot hub and runs a test on a real device. In this test, we'll test a sample calculator app by entering values into two input fields and verifying the sum.
import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import org.junit.jupiter.api.*;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class AndroidAppTest {
private AndroidDriver driver;
@BeforeEach
public void setUp() throws MalformedURLException {
// Configure capabilities using UiAutomator2Options
UiAutomator2Options options = new UiAutomator2Options()
.setPlatformName("Android")
.setPlatformVersion("16.0")
.setDeviceName("Pixel 9")
.setApp("tb://<your-app-id>")
.setAutomationName("UiAutomator2");
// Add TestingBot-specific options
options.setCapability("tb:options", java.util.Map.of(
"key", "YOUR_TB_KEY",
"secret", "YOUR_TB_SECRET",
"name", "Android Calculator Test",
"build", "JUnit Build 1"
));
// Connect to TestingBot hub
driver = new AndroidDriver(
new URL("https://hub.testingbot.com/wd/hub"),
options
);
}
@Test
public void testCalculatorSum() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
// Enter first number
WebElement inputA = wait.until(
ExpectedConditions.elementToBeClickable(AppiumBy.accessibilityId("inputA"))
);
inputA.sendKeys("10");
// Enter second number
WebElement inputB = wait.until(
ExpectedConditions.elementToBeClickable(AppiumBy.accessibilityId("inputB"))
);
inputB.sendKeys("5");
// Verify the sum
WebElement sum = wait.until(
ExpectedConditions.elementToBeClickable(AppiumBy.accessibilityId("sum"))
);
assertEquals("15", sum.getText());
}
@AfterEach
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
import io.appium.java_client.AppiumBy;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.options.XCUITestOptions;
import org.junit.jupiter.api.*;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class IOSAppTest {
private IOSDriver driver;
@BeforeEach
public void setUp() throws MalformedURLException {
// Configure capabilities using XCUITestOptions
XCUITestOptions options = new XCUITestOptions()
.setPlatformName("iOS")
.setPlatformVersion("26.0")
.setDeviceName("iPhone 17")
.setApp("tb://<your-app-id>")
.setAutomationName("XCUITest");
// Add TestingBot-specific options
options.setCapability("tb:options", java.util.Map.of(
"key", "YOUR_TB_KEY",
"secret", "YOUR_TB_SECRET",
"name", "iOS Calculator Test",
"build", "JUnit Build 1"
));
// Connect to TestingBot hub
driver = new IOSDriver(
new URL("https://hub.testingbot.com/wd/hub"),
options
);
}
@Test
public void testCalculatorSum() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
// Enter first number
WebElement inputA = wait.until(
ExpectedConditions.elementToBeClickable(AppiumBy.accessibilityId("inputA"))
);
inputA.sendKeys("10");
// Enter second number
WebElement inputB = wait.until(
ExpectedConditions.elementToBeClickable(AppiumBy.accessibilityId("inputB"))
);
inputB.sendKeys("5");
// Verify the sum
WebElement sum = wait.until(
ExpectedConditions.elementToBeClickable(AppiumBy.accessibilityId("sum"))
);
assertEquals("15", sum.getText());
}
@AfterEach
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
Always call driver.quit() in your teardown method to properly end the session. Otherwise, the test will continue running until it times out.
Uploading Your App
Please see our Upload Mobile App documentation to find out how to upload your app to TestingBot for testing.
Specify Browsers & Devices
To run your tests on TestingBot, you need to configure the device and platform you want to test on. The Appium Java client provides type-safe Options classes for this purpose.
Using Options Classes (Recommended)
For Android testing, use UiAutomator2Options. For iOS testing, use XCUITestOptions:
UiAutomator2Options options = new UiAutomator2Options()
.setPlatformName("Android")
.setPlatformVersion("16.0")
.setDeviceName("Pixel 9")
.setApp("tb://<your-app-id>")
.setAutomationName("UiAutomator2");
// Add TestingBot credentials
options.setCapability("tb:options", Map.of(
"key", "YOUR_TB_KEY",
"secret", "YOUR_TB_SECRET",
"name", "My Test",
"build", "Build 1"
));
AndroidDriver driver = new AndroidDriver(
new URL("https://hub.testingbot.com/wd/hub"),
options
);
XCUITestOptions options = new XCUITestOptions()
.setPlatformName("iOS")
.setPlatformVersion("26.0")
.setDeviceName("iPhone 17")
.setApp("tb://<your-app-id>")
.setAutomationName("XCUITest");
// Add TestingBot credentials
options.setCapability("tb:options", Map.of(
"key", "YOUR_TB_KEY",
"secret", "YOUR_TB_SECRET",
"name", "My Test",
"build", "Build 1"
));
IOSDriver driver = new IOSDriver(
new URL("https://hub.testingbot.com/wd/hub"),
options
);
Using BaseOptions (Generic)
For more flexibility or when working with multiple platforms, you can use BaseOptions:
import io.appium.java_client.remote.options.BaseOptions;
BaseOptions options = new BaseOptions()
.setPlatformName("Android")
.setAutomationName("UiAutomator2")
.amend("appium:deviceName", "Pixel 9")
.amend("appium:platformVersion", "16.0")
.amend("appium:app", "tb://<your-app-id>");
options.setCapability("tb:options", Map.of(
"key", "YOUR_TB_KEY",
"secret", "YOUR_TB_SECRET"
));
AppiumDriver driver = new AppiumDriver(
new URL("https://hub.testingbot.com/wd/hub"),
options
);
Required Capabilities
To specify which device you want to test on, you need to provide:
-
platformName- The mobile OS (Android or iOS) -
appium:platformVersion- The OS version (e.g., "17.0", "26.0") -
appium:deviceName- The device name (e.g., "Pixel 9", "iPhone 17") -
appium:app- The app to test (URL or tb:// reference) -
appium:automationName- The automation driver ("UiAutomator2" for Android, "XCUITest" for iOS)
Use the device picker below to generate the configuration for your target device:
Testing Internal Websites
We've built TestingBot Tunnel, to provide you with a secure way to run tests against your staged/internal webapps.
Please see our TestingBot Tunnel documentation for more information about this easy to use tunneling solution.
The example below shows how to easily run a JUnit test with our Tunnel:
1. Download our tunnel and start the tunnel:
java -jar testingbot-tunnel.jar key secret
2. Update your test to connect through the tunnel instead of directly to the TestingBot hub.
Change the URL from hub.testingbot.com to localhost:4445:
// Connect through the tunnel instead of directly to TestingBot
AndroidDriver driver = new AndroidDriver(
new URL("http://localhost:4445/wd/hub"),
options
);
Your test traffic will now go securely through the tunnel, allowing you to test apps that access internal resources.
Run tests in Parallel
Parallel Testing means running the same test, or multiple tests, simultaneously. This greatly reduces your total testing time.
You can run the same tests on all different browser configurations or run different tests all on the same browser configuration.
TestingBot has a large grid of machines and browsers, which means you can use our service to do efficient parallel testing. It is one of the key features we provide to greatly cut down on your total testing time.
Please see our Parallel JUnit documentation for parallel testing.
Queuing
Every plan we provide comes with a limit of parallel tests.
If you exceed the number of parallel tests assigned to your account, TestingBot will queue the additional tests (for up to 6 minutes) and run the tests as soon as slots become available.
Mark tests as passed/failed
TestingBot cannot automatically determine if your test passed or failed - that depends on your test assertions. To report test results to TestingBot, use our REST API.
First, add the TestingBot Java client to your dependencies:
<dependency>
<groupId>com.testingbot</groupId>
<artifactId>testingbotrest</artifactId>
<version>1.0.10</version>
</dependency>
Then use it in your test to report the result:
import com.testingbot.testingbotrest.TestingbotREST;
import java.util.Map;
@AfterEach
public void tearDown() {
if (driver != null) {
// Report test result to TestingBot
TestingbotREST api = new TestingbotREST(
"YOUR_TB_KEY",
"YOUR_TB_SECRET"
);
api.updateTest(
driver.getSessionId().toString(),
Map.of(
"success", "1", // "1" for passed, "0" for failed
"name", "Calculator Test"
)
);
driver.quit();
}
}
For JUnit 5, you can use a TestWatcher extension to automatically report results:
import org.junit.jupiter.api.extension.*;
public class TestingBotWatcher implements TestWatcher {
private final AndroidDriver driver;
private final TestingbotREST api;
public TestingBotWatcher(AndroidDriver driver, String key, String secret) {
this.driver = driver;
this.api = new TestingbotREST(key, secret);
}
@Override
public void testSuccessful(ExtensionContext context) {
updateTest("1", context.getDisplayName());
}
@Override
public void testFailed(ExtensionContext context, Throwable cause) {
updateTest("0", context.getDisplayName());
}
private void updateTest(String success, String name) {
api.updateTest(driver.getSessionId().toString(), Map.of(
"success", success,
"name", name
));
}
}
Preparing your App
You do not need to install any code or plugin to run a test.
Below are some things that are necessary to successfully run a mobile test:
- Please supply the URL to your
.apkor.aabfile.
Important: the.apkfile needs to be a x86 build (x86 ABI) for Android emulators.
- Please supply the URL to an
.ipafile.
- Please supply the URL to a
.zipfile that contains your.app -
The
.appneeds to be an iOS Simulator build:
- Create a Simulator build:Copy
xcodebuild -sdk iphonesimulator -configuration Debug - Zip the
.appbundle:Copyzip -r MyApp.zip MyApp.app
- Create a Simulator build:
Additional Options
Appium provides a lot of options to configure your test.
Some important options that might help:
For Android:- appActivity and appPackage: by default, Appium will try to extract the main Activity from your apk. If this fails, you can supply your own with these options.
- chromeOptions: additional chromedriver options you can supply.
- otherApps: a JSON array of other apps that need to be installed, in URL format.
-
locale: the language of the simulator/device (ex:
fr_CA)This sets the locale on the entire device/simulator, except on physical iOS devices. For real iOS devices, we will pass
-AppleLocaleas argument to the app. - newCommandTimeout: How long (in seconds) Appium will wait for a new command from the client before assuming the client quit and ending the session