---
title: Mobile App Instrumentation with Appium | TestingBot
description: Instrument mobile apps for testing with Appium. Perform image injection,
  biometric authentication, mock sms messages.
source_url:
  html: https://testingbot.com/support/app-automate/appium/instrumentation
  md: https://testingbot.com/support/app-automate/appium/instrumentation.md
---

# App Instrumentation with Appium

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](https://testingbot.com#image-injection): feed a test image to the virtual camera instead of using a real one.
- [Biometric authentication](https://testingbot.com#biometric): simulate fingerprint or Touch ID prompts in your tests.
- [Receive calls and SMS messages](https://testingbot.com#calls-sms): emulate incoming phone calls or text messages to see how your app responds.

## Injecting images into camera
Android Emulators Only

Image 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.

[Java](https://testingbot.com#) [NodeJS](https://testingbot.com#) [Python](https://testingbot.com#) [Ruby](https://testingbot.com#) [C#](https://testingbot.com#)

```java
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();
    }
}
```

```javascript
const { remote } = require('webdriverio');

async function imageToBase64(url) {
  const res = await fetch(url);
  const buffer = Buffer.from(await res.arrayBuffer());
  return buffer.toString('base64');
}

(async () => {
  const caps = {
    platformName: 'Android',
    'appium:deviceName': 'Pixel 9',
    browserName: 'chrome',
    browserVersion: '15',
    'tb:options': { 'appiumVersion': '2.11.3', 'appiumPlugins': ['images'] },
    '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();
})();
```

```python
from appium import webdriver
from appium.options.android import UiAutomator2Options
import base64
import requests
import time

def image_to_base64(url):
    response = requests.get(url)
    return base64.b64encode(response.content).decode('utf-8')

options = UiAutomator2Options().load_capabilities({
    "platformName": "Android",
    "browserName": "chrome",
    "browserVersion": "15",
    "appium:deviceName": "Pixel 9",
    "appium:injectedImageProperties": {},
    "tb:options": {
        "appiumVersion": "2.11.3",
        "appiumPlugins": ["images"]
    }
})

driver = webdriver.Remote("https://key:secret@hub.testingbot.com/wd/hub", options=options)

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()
```

```ruby
#!/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
```

```csharp
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Remote;
using System;
using System.Collections.Generic;
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.AddAdditionalAppiumOption("browserName", "chrome");
        options.AddAdditionalAppiumOption("browserVersion", "15");
        options.AddAdditionalAppiumOption("appium:deviceName", "Pixel 9");
        options.AddAdditionalAppiumOption("tb:appiumPlugins", new string[] { "images" });
        options.AddAdditionalAppiumOption("tb:appiumVersion", "2.11.3");
        options.AddAdditionalAppiumOption("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](https://github.com/appium/appium-uiautomator2-driver?tab=readme-ov-file#mobile-fingerprint) command. Only works on API 23+ with [Appium version 2 or higher](https://testingbot.com/support/app-automate/appium/appium-versions).

[Java](https://testingbot.com#) [NodeJS](https://testingbot.com#) [Python](https://testingbot.com#) [Ruby](https://testingbot.com#) [C#](https://testingbot.com#)

```java
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();
    }
}
```

```javascript
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();
})();
```

```python
from appium import webdriver
from appium.options.android import UiAutomator2Options
import time

options = UiAutomator2Options().load_capabilities({
    "platformName": "Android",
    "appium:deviceName": "Pixel 9",
    "browserName": "chrome",
    "browserVersion": "15",
    "tb:options": {"appiumVersion": "2.11.3"}
})

driver = webdriver.Remote("https://key:secret@hub.testingbot.com/wd/hub", options=options)

# Simulate fingerprint auth with ID 1
driver.execute_script("mobile: fingerprint", { "fingerprintId": 1 })

time.sleep(5)
driver.quit()
```

```ruby
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
```

```csharp
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.AddAdditionalAppiumOption("appium:deviceName", "Pixel 9");
        options.AddAdditionalAppiumOption("browserName", "chrome");
        options.AddAdditionalAppiumOption("browserVersion", "15");
        options.AddAdditionalAppiumOption("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](https://appium.github.io/appium-xcuitest-driver/latest/reference/execute-methods/#mobile-sendbiometricmatch) command.

[Java](https://testingbot.com#) [NodeJS](https://testingbot.com#) [Python](https://testingbot.com#) [Ruby](https://testingbot.com#) [C#](https://testingbot.com#)

```java
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("appium: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();
    }
}
```

```javascript
const { remote } = require('webdriverio');

(async () => {
  const caps = {
    platformName: 'iOS',
    'appium:deviceName': 'iPhone 15',
    'appium: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();
})();
```

```python
from appium import webdriver
from appium.options.ios import XCUITestOptions
import time

options = XCUITestOptions().load_capabilities({
    "platformName": "iOS",
    "appium:deviceName": "iPhone 15",
    "appium:platformVersion": "18.0",
    "browserName": "safari",
    "tb:options": {"appiumVersion": "2.11.3"}
})

driver = webdriver.Remote("https://key:secret@hub.testingbot.com/wd/hub", options=options)

# 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()
```

```ruby
require 'appium_lib'

caps = {
  "platformName" => "iOS",
  "appium:deviceName" => "iPhone 15",
  "appium: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
```

```csharp
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.AddAdditionalAppiumOption("appium:deviceName", "iPhone 15");
        options.AddAdditionalAppiumOption("appium:platformVersion", "18.0");
        options.AddAdditionalAppiumOption("browserName", "safari");
        options.AddAdditionalAppiumOption("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.

[Java](https://testingbot.com#) [NodeJS](https://testingbot.com#) [Python](https://testingbot.com#) [Ruby](https://testingbot.com#) [C#](https://testingbot.com#)

```java
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();
    }
}
```

```javascript
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();
})();
```

```python
from appium import webdriver
from appium.options.android import UiAutomator2Options
import time

options = UiAutomator2Options().load_capabilities({
    "platformName": "Android",
    "appium:deviceName": "Pixel 9",
    "browserName": "chrome",
    "browserVersion": "15",
    "tb:options": {"appiumVersion": "2.11.3"}
})

driver = webdriver.Remote("https://key:secret@hub.testingbot.com/wd/hub", options=options)

# Send SMS
driver.send_sms("5555555555", "Your message here!")

# Phone call
driver.make_gsm_call("5555555555", "call")
time.sleep(2)
driver.make_gsm_call("5555555555", "accept")
time.sleep(2)
driver.make_gsm_call("5555555555", "cancel")

driver.quit()
```

```ruby
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
```

```csharp
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.AddAdditionalAppiumOption("appium:deviceName", "Pixel 9");
        options.AddAdditionalAppiumOption("browserName", "chrome");
        options.AddAdditionalAppiumOption("browserVersion", "15");
        options.AddAdditionalAppiumOption("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();
    }
}
```

### Looking for more help?

Have questions or need more information? Reach out via email or Slack.

[Email us](https://testingbot.com/contact/new) [Join our Slack](https://join.slack.com/t/testingb0t/shared_invite/zt-3bcw9xch-jk19~6XPs_xBrsAgAedkCw)
