Test App Upgrades/Mid-Session App Installations
App instrumentation means adding extra hooks to your app while it runs on TestingBot's devices. It lets your automated tests interact with things that normally require human interaction, such as authentication, taking a specific photo or making a call.
With app instrumentation, you can perform the following tasks in an automated Appium test script:
- Inject images into the camera: feed a test image to the virtual camera instead of using a real one.
- Biometric authentication: simulate fingerprint or Touch ID prompts in your tests.
- Receive calls and SMS messages: emulate incoming phone calls or text messages to see how your app responds.
Injecting images into camera
Android Emulators OnlyImage injection allows you to specify a base64 encoded image to the virtual camera of the Android emulator.
The test script will fetch the content of the image, convert it to base64 and send it to the TestingBot grid. The image will be used by the remote device to inject into the camera.
Make sure to include the images
Appium plugin, as it is required for this to function correctly.
import io.appium.java_client.AppiumDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
import java.util.Base64;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
public class ImageInjectionExample {
public static String imageToBase64(String url) throws Exception {
byte[] data = java.net.URI.create(url).toURL().openStream().readAllBytes();
return Base64.getEncoder().encodeToString(data);
}
public static void main(String[] args) throws Exception {
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("platformName", "Android");
caps.setCapability("browserName", "chrome");
caps.setCapability("browserVersion", "15");
caps.setCapability("appium:deviceName", "Pixel 9");
caps.setCapability("tb:appiumPlugins", new String[]{"images"});
caps.setCapability("tb:appiumVersion", "2.11.3");
caps.setCapability("appium:injectedImageProperties", new HashMap<>());
AppiumDriver driver = new AppiumDriver(new URL("https://key:secret@hub.testingbot.com/wd/hub"), caps);
String base64Image = imageToBase64("https://upload.wikimedia.org/wikipedia/commons/5/5e/QR_Code_example.png");
driver.executeScript("mobile: injectEmulatorCameraImage", new HashMap<String, Object>() {{
put("payload", base64Image);
}});
Thread.sleep(30000);
base64Image = imageToBase64("https://testingbot.com/assets/about.png");
driver.executeScript("mobile: injectEmulatorCameraImage", new HashMap<String, Object>() {{
put("payload", base64Image);
}});
Thread.sleep(30000);
driver.quit();
}
}
const { remote } = require('webdriverio');
const fs = require('fs');
const fetch = require('node-fetch');
async function imageToBase64(url) {
const res = await fetch(url);
const buffer = await res.buffer();
return buffer.toString('base64');
}
(async () => {
const caps = {
platformName: 'Android',
'appium:deviceName': 'Pixel 9',
browserName: 'chrome',
browserVersion: '15',
'tb:appiumPlugins': ['images'],
'tb:appiumVersion': '2.11.3',
'appium:injectedImageProperties': {}
};
const driver = await remote({
protocol: 'https',
hostname: 'hub.testingbot.com',
port: 443,
path: '/wd/hub',
user: 'key',
key: 'secret',
capabilities: caps
});
let base64Image = await imageToBase64('https://upload.wikimedia.org/wikipedia/commons/5/5e/QR_Code_example.png');
await driver.executeScript('mobile: injectEmulatorCameraImage', [{ payload: base64Image }]);
console.log("Injected first image");
await driver.pause(30000);
base64Image = await imageToBase64('https://testingbot.com/assets/about.png');
await driver.executeScript('mobile: injectEmulatorCameraImage', [{ payload: base64Image }]);
console.log("Injected second image");
await driver.pause(30000);
await driver.deleteSession();
})();
from appium import webdriver
import base64
import requests
import time
def image_to_base64(url):
response = requests.get(url)
return base64.b64encode(response.content).decode('utf-8')
caps = {
"platformName": "Android",
"browserName": "chrome",
"browserVersion": "15",
"appium:deviceName": "Pixel 9",
"tb:appiumPlugins": ["images"],
"tb:appiumVersion": "2.11.3",
"appium:injectedImageProperties": {}
}
driver = webdriver.Remote("https://key:secret@hub.testingbot.com/wd/hub", caps)
base64_image = image_to_base64("https://upload.wikimedia.org/wikipedia/commons/5/5e/QR_Code_example.png")
driver.execute_script("mobile: injectEmulatorCameraImage", { "payload": base64_image })
time.sleep(30)
print("Changing image")
base64_image = image_to_base64("https://testingbot.com/assets/about.png")
driver.execute_script("mobile: injectEmulatorCameraImage", { "payload": base64_image })
time.sleep(30)
driver.quit()
#!/usr/bin/env ruby
require 'rubygems'
require 'appium_lib'
require 'base64'
require 'open-uri'
def image_to_base64(image_url)
image_data = URI.parse(image_url).open.read
Base64.strict_encode64(image_data)
end
caps = {
"browserName" => "chrome",
"browserVersion" => "15",
"appium:deviceName" => "Pixel 9",
"platformName" => "Android",
"tb:appiumPlugins" => ["images"],
"tb:appiumVersion" => "2.11.3",
"appium:injectedImageProperties" => {}
}
appium_driver = Appium::Driver.new({
caps: caps,
appium_lib: {
server_url: "https://key:secret@hub.testingbot.com/wd/hub"
}
}, true)
driver = appium_driver.start_driver
base64_image = image_to_base64("https://upload.wikimedia.org/wikipedia/commons/5/5e/QR_Code_example.png")
driver.execute_script('mobile: injectEmulatorCameraImage', { payload: base64_image })
sleep 30
puts "Changing image"
base64_image = image_to_base64("https://testingbot.com/assets/about.png")
driver.execute_script('mobile: injectEmulatorCameraImage', { payload: base64_image })
sleep 30
driver.quit
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Remote;
using System;
using System.Net.Http;
using System.Threading.Tasks;
class ImageInjectionExample
{
static async Task<string> ImageToBase64(string url)
{
using (HttpClient client = new HttpClient())
{
var data = await client.GetByteArrayAsync(url);
return Convert.ToBase64String(data);
}
}
static async Task Main(string[] args)
{
var options = new AppiumOptions();
options.PlatformName = "Android";
options.AddAdditionalCapability("browserName", "chrome");
options.AddAdditionalCapability("browserVersion", "15");
options.AddAdditionalCapability("appium:deviceName", "Pixel 9");
options.AddAdditionalCapability("tb:appiumPlugins", new string[] { "images" });
options.AddAdditionalCapability("tb:appiumVersion", "2.11.3");
options.AddAdditionalCapability("appium:injectedImageProperties", new { });
var driver = new RemoteWebDriver(
new Uri("https://key:secret@hub.testingbot.com/wd/hub"),
options
);
var base64Image = await ImageToBase64("https://upload.wikimedia.org/wikipedia/commons/5/5e/QR_Code_example.png");
driver.ExecuteScript("mobile: injectEmulatorCameraImage", new Dictionary<string, object> { { "payload", base64Image } });
Console.WriteLine("Injected first image");
System.Threading.Thread.Sleep(30000);
base64Image = await ImageToBase64("https://testingbot.com/assets/about.png");
driver.ExecuteScript("mobile: injectEmulatorCameraImage", new Dictionary<string, object> { { "payload", base64Image } });
Console.WriteLine("Injected second image");
System.Threading.Thread.Sleep(30000);
driver.Quit();
}
}
Biometric authentication (Finger Print, Touch Id)
Android fingerprint authentication
Emulate fingerprints on Android Emulator, with the mobile:fingerprint command. Only works on API 23+ with Appium version 2 or higher.
import io.appium.java_client.AppiumDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
import java.util.HashMap;
public class FingerprintExample {
public static void main(String[] args) throws Exception {
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("platformName", "Android");
caps.setCapability("appium:deviceName", "Pixel 9");
caps.setCapability("browserName", "chrome");
caps.setCapability("browserVersion", "15");
caps.setCapability("tb:appiumVersion", "2.11.3");
AppiumDriver driver = new AppiumDriver(new URL("https://key:secret@hub.testingbot.com/wd/hub"), caps);
HashMap<String, Object> args1 = new HashMap<>();
args1.put("fingerprintId", 1);
driver.executeScript("mobile: fingerprint", args1);
Thread.sleep(5000);
driver.quit();
}
}
const { remote } = require('webdriverio');
(async () => {
const caps = {
platformName: 'Android',
'appium:deviceName': 'Pixel 9',
browserName: 'chrome',
browserVersion: '15',
'tb:appiumVersion': '2.11.3'
};
const driver = await remote({
protocol: 'https',
hostname: 'hub.testingbot.com',
port: 443,
path: '/wd/hub',
user: 'key',
key: 'secret',
capabilities: caps
});
await driver.executeScript('mobile: fingerprint', [{ fingerprintId: 1 }]);
await driver.pause(5000);
await driver.deleteSession();
})();
from appium import webdriver
import time
caps = {
"platformName": "Android",
"appium:deviceName": "Pixel 9",
"browserName": "chrome",
"browserVersion": "15",
"tb:appiumVersion": "2.11.3"
}
driver = webdriver.Remote("https://key:secret@hub.testingbot.com/wd/hub", caps)
# Simulate fingerprint auth with ID 1
driver.execute_script("mobile: fingerprint", { "fingerprintId": 1 })
time.sleep(5)
driver.quit()
require 'appium_lib'
caps = {
"platformName" => "Android",
"appium:deviceName" => "Pixel 9",
"browserName" => "chrome",
"browserVersion" => "15",
"tb:appiumVersion" => "2.11.3"
}
driver = Appium::Driver.new({
caps: caps,
appium_lib: {
server_url: "https://key:secret@hub.testingbot.com/wd/hub"
}
}, true).start_driver
driver.execute_script("mobile: fingerprint", { fingerprintId: 1 })
sleep 5
driver.quit
using OpenQA.Selenium.Remote;
using System;
using System.Collections.Generic;
using OpenQA.Selenium.Appium;
class FingerprintExample {
static void Main(string[] args) {
var options = new AppiumOptions();
options.PlatformName = "Android";
options.AddAdditionalCapability("appium:deviceName", "Pixel 9");
options.AddAdditionalCapability("browserName", "chrome");
options.AddAdditionalCapability("browserVersion", "15");
options.AddAdditionalCapability("tb:appiumVersion", "2.11.3");
var driver = new RemoteWebDriver(
new Uri("https://key:secret@hub.testingbot.com/wd/hub"), options);
driver.ExecuteScript("mobile: fingerprint", new Dictionary<string, object> {
{ "fingerprintId", 1 }
});
System.Threading.Thread.Sleep(5000);
driver.Quit();
}
}
iOS TouchID/FaceID authentication
You can simulate a biometric match/non-match event with TouchID and FaceID, with the mobile: sendBiometricMatch command.
import io.appium.java_client.AppiumDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
import java.util.HashMap;
public class TouchIDExample {
public static void main(String[] args) throws Exception {
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("platformName", "iOS");
caps.setCapability("appium:deviceName", "iPhone 15");
caps.setCapability("platformVersion", "18.0");
caps.setCapability("browserName", "safari");
caps.setCapability("tb:appiumVersion", "2.11.3");
AppiumDriver driver = new AppiumDriver(new URL("https://key:secret@hub.testingbot.com/wd/hub"), caps);
HashMap<String, Object> args1 = new HashMap<>();
args1.put("type", "touchId");
args1.put("match", true);
driver.executeScript("mobile: sendBiometricMatch", args1);
Thread.sleep(5000);
HashMap<String, Object> args2 = new HashMap<>();
args2.put("type", "touchId");
args2.put("match", false);
driver.executeScript("mobile: sendBiometricMatch", args2);
Thread.sleep(5000);
driver.quit();
}
}
const { remote } = require('webdriverio');
(async () => {
const caps = {
platformName: 'iOS',
'appium:deviceName': 'iPhone 15',
platformVersion: '18.0',
browserName: 'safari',
'tb:appiumVersion': '2.11.3'
};
const driver = await remote({
protocol: 'https',
hostname: 'hub.testingbot.com',
port: 443,
path: '/wd/hub',
user: 'key',
key: 'secret',
capabilities: caps
});
// Successful Touch ID
await driver.executeScript('mobile: sendBiometricMatch', [{ type: 'touchId', match: true }]);
await driver.pause(5000);
// Failed Touch ID
await driver.executeScript('mobile: sendBiometricMatch', [{ type: 'touchId', match: false }]);
await driver.pause(5000);
await driver.deleteSession();
})();
from appium import webdriver
import time
caps = {
"platformName": "iOS",
"appium:deviceName": "iPhone 15",
"platformVersion": "18.0",
"browserName": "safari",
"tb:appiumVersion": "2.11.3"
}
driver = webdriver.Remote("https://key:secret@hub.testingbot.com/wd/hub", caps)
# Successful Touch ID
driver.execute_script("mobile: sendBiometricMatch", { "type": "touchId", "match": True })
time.sleep(5)
# Failed Touch ID
driver.execute_script("mobile: sendBiometricMatch", { "type": "touchId", "match": False })
time.sleep(5)
driver.quit()
require 'appium_lib'
caps = {
"platformName" => "iOS",
"appium:deviceName" => "iPhone 15",
"platformVersion" => "18.0",
"browserName" => "safari",
"tb:appiumVersion" => "2.11.3"
}
driver = Appium::Driver.new({
caps: caps,
appium_lib: {
server_url: "https://key:secret@hub.testingbot.com/wd/hub"
}
}, true).start_driver
# Success
driver.execute_script("mobile: sendBiometricMatch", { type: "touchId", match: true })
sleep 5
# Failure
driver.execute_script("mobile: sendBiometricMatch", { type: "touchId", match: false })
sleep 5
driver.quit
using OpenQA.Selenium.Remote;
using System;
using System.Collections.Generic;
using OpenQA.Selenium.Appium;
class TouchIDExample {
static void Main(string[] args) {
var options = new AppiumOptions();
options.PlatformName = "iOS";
options.AddAdditionalCapability("appium:deviceName", "iPhone 15");
options.AddAdditionalCapability("platformVersion", "18.0");
options.AddAdditionalCapability("browserName", "safari");
options.AddAdditionalCapability("tb:appiumVersion", "2.11.3");
var driver = new RemoteWebDriver(
new Uri("https://key:secret@hub.testingbot.com/wd/hub"), options);
driver.ExecuteScript("mobile: sendBiometricMatch", new Dictionary<string, object> {
{ "type", "touchId" },
{ "match", true }
});
System.Threading.Thread.Sleep(5000);
driver.ExecuteScript("mobile: sendBiometricMatch", new Dictionary<string, object> {
{ "type", "touchId" },
{ "match", false }
});
System.Threading.Thread.Sleep(5000);
driver.Quit();
}
}
Receiving calls and text messages (SMS)
You can simulate an incoming call or text message with Appium. Please see the examples below.
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.connection.GsmCallActions;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
public class AndroidGsmExample {
public static void main(String[] args) throws Exception {
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("platformName", "Android");
caps.setCapability("appium:deviceName", "Pixel 9");
caps.setCapability("browserName", "chrome");
caps.setCapability("browserVersion", "15");
caps.setCapability("tb:appiumVersion", "2.11.3");
AndroidDriver driver = new AndroidDriver(new URL("https://key:secret@hub.testingbot.com/wd/hub"), caps);
// Send SMS
driver.sendSMS("5555555555", "Your message here!");
// Make and handle a phone call
driver.makeGsmCall("5555555555", GsmCallActions.CALL);
Thread.sleep(2000);
driver.makeGsmCall("5555555555", GsmCallActions.ACCEPT);
Thread.sleep(2000);
driver.makeGsmCall("5555555555", GsmCallActions.CANCEL);
driver.quit();
}
}
const { remote } = require('webdriverio');
(async () => {
const caps = {
platformName: 'Android',
'appium:deviceName': 'Pixel 9',
browserName: 'chrome',
browserVersion: '15',
'tb:appiumVersion': '2.11.3'
};
const driver = await remote({
protocol: 'https',
hostname: 'hub.testingbot.com',
port: 443,
path: '/wd/hub',
user: 'key',
key: 'secret',
capabilities: caps
});
// Send SMS
await driver.sendSms('5555555555', 'Your message here!');
// Phone call
await driver.makeGsmCall('5555555555', 'call');
await driver.pause(2000);
await driver.makeGsmCall('5555555555', 'accept');
await driver.pause(2000);
await driver.makeGsmCall('5555555555', 'cancel');
await driver.deleteSession();
})();
from appium import webdriver
import time
caps = {
"platformName": "Android",
"appium:deviceName": "Pixel 9",
"browserName": "chrome",
"browserVersion": "15",
"tb:appiumVersion": "2.11.3"
}
driver = webdriver.Remote("https://key:secret@hub.testingbot.com/wd/hub", caps)
# Send SMS
driver.send_sms(phone_number: "5555555555", message: "Your message here!")
# Phone call
driver.gsm_call(phone_number: "5555555555", action: :call)
sleep 2
driver.gsm_call(phone_number: "5555555555", action: :accept)
sleep 2
driver.gsm_call(phone_number: "5555555555", action: :cancel)
driver.quit()
require 'appium_lib'
caps = {
"platformName" => "Android",
"appium:deviceName" => "Pixel 9",
"browserName" => "chrome",
"browserVersion" => "15",
"tb:appiumVersion" => "2.11.3"
}
driver = Appium::Driver.new({
caps: caps,
appium_lib: {
server_url: "https://key:secret@hub.testingbot.com/wd/hub"
}
}, true).start_driver
# Send SMS
driver.send_sms("5555555555", "Your message here!")
# Phone call
driver.make_gsm_call("5555555555", "call")
sleep 2
driver.make_gsm_call("5555555555", "accept")
sleep 2
driver.make_gsm_call("5555555555", "cancel")
driver.quit
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using System;
class AndroidGsmExample {
static void Main(string[] args) {
var options = new AppiumOptions();
options.PlatformName = "Android";
options.AddAdditionalCapability("appium:deviceName", "Pixel 9");
options.AddAdditionalCapability("browserName", "chrome");
options.AddAdditionalCapability("browserVersion", "15");
options.AddAdditionalCapability("tb:appiumVersion", "2.11.3");
var driver = new AndroidDriver(new Uri("https://key:secret@hub.testingbot.com/wd/hub"), options);
// Send SMS
driver.SendSMS("5555555555", "Your message here!");
// Phone call
driver.MakeGsmCall("5555555555", "call");
System.Threading.Thread.Sleep(2000);
driver.MakeGsmCall("5555555555", "accept");
System.Threading.Thread.Sleep(2000);
driver.MakeGsmCall("5555555555", "cancel");
driver.Quit();
}
}