---
title: Testing Push Notifications with Appium on TestingBot
description: How to test push notifications on Android and iOS with Appium on TestingBot.
  Send simulated push notifications to real devices and emulators during automated
  mobile testing.
source_url:
  html: https://testingbot.com/support/app-automate/appium/push-notifications
  md: https://testingbot.com/support/app-automate/appium/push-notifications/index.md
---
# Testing Push Notifications with Appium

Push notifications are a critical part of many mobile applications. With TestingBot, you can simulate and interact with push notifications on both Android and iOS devices during your Appium tests.

**Prerequisite:** Your app must request push notification permission and the user must allow it before notifications can be displayed. To handle this automatically during tests, set the `autoGrantPermissions` capability (Android) or `autoAcceptAlerts` capability (iOS). If your app has not registered for notifications, none of the approaches below will show a visible notification.

    // Android: auto-grant all permissions including notifications
    options.setCapability("appium:autoGrantPermissions", true);
    
    // iOS: auto-accept permission dialogs (e.g. "Allow Notifications")
    options.setCapability("appium:autoAcceptAlerts", true);

## Which method should I use?

- **[Android](https://testingbot.com#android)**, Trigger real push notifications via FCM or use ADB shell commands via Appium's `mobile: shell` on emulators and real devices.
- **[iOS Simulators](https://testingbot.com#ios-simulator)**, Use Appium's `mobile: pushNotification` command to send simulated notifications with a JSON payload.
- **[iOS Real Devices](https://testingbot.com#ios-real)**, Trigger push notifications via your backend server or a third-party service (APNs, Firebase) to the device under test.
- **[Interact with Notifications](https://testingbot.com#interact)**, Open the notification shade or banner and tap on notifications during your test.

## Android Push Notifications

There are two approaches to test push notifications on Android with TestingBot:

Trigger via your backend (recommended)Send a real push notification through FCM (Firebase Cloud Messaging) to the app running on the device. The notification appears with the correct app icon and package attribution, exactly as your end users would see it.Simulate via ADB shellUse Appium's `mobile: shell` command to post a system notification. This is a quick way to test notification interaction (tapping, swiping), but the notification appears as a generic system notification without your app's icon.
### Option 1: Trigger a real notification via FCM (recommended)

If your app uses Firebase Cloud Messaging, you can send a test push notification by calling the FCM HTTP v1 API during your test. The notification will be delivered to your app on the TestingBot device, showing the correct app icon and notification channel.

You'll need a **Firebase service account key** (JSON file) for your project and the **FCM registration token** from the device. Most apps log the registration token on startup, you can retrieve it from logcat or have your app expose it via a test endpoint.

Your app must already be set up to receive FCM notifications. This approach sends a real notification through your existing push infrastructure.

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

    import io.appium.java_client.android.AndroidDriver;
    import io.appium.java_client.android.options.UiAutomator2Options;
    
    import java.net.URL;
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.util.HashMap;
    import java.util.Map;
    
    public class PushNotificationTest {
      public static void main(String[] args) throws Exception {
        UiAutomator2Options options = new UiAutomator2Options();
        options.setDeviceName("Pixel 8");
        options.setPlatformVersion("14.0");
        options.setApp("tb://your_app_id");
    
        Map<String, Object> tbOptions = new HashMap<>();
        tbOptions.put("key", "api_key");
        tbOptions.put("secret", "api_secret");
        tbOptions.put("name", "Push Notification Test");
        tbOptions.put("realDevice", true);
        options.setCapability("tb:options", tbOptions);
    
        AndroidDriver driver = new AndroidDriver(
            new URL("https://hub.testingbot.com/wd/hub"), options);
    
        // Retrieve the FCM registration token from your app
        // (e.g. via a test endpoint or logcat)
        String fcmToken = "DEVICE_FCM_REGISTRATION_TOKEN";
    
        // Send a real push notification via FCM HTTP v1 API
        String projectId = "your-firebase-project-id";
        String accessToken = "YOUR_OAUTH2_ACCESS_TOKEN";
    
        String json = "{"
            + "\"message\": {"
            + " \"token\": \"" + fcmToken + "\","
            + " \"notification\": {"
            + " \"title\": \"Order Update\","
            + " \"body\": \"Your order has been shipped!\""
            + " }"
            + "}"
            + "}";
    
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://fcm.googleapis.com/v1/projects/"
                + projectId + "/messages:send"))
            .header("Authorization", "Bearer " + accessToken)
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(json))
            .build();
    
        HttpClient.newHttpClient().send(request,
            HttpResponse.BodyHandlers.ofString());
    
        Thread.sleep(3000);
        driver.quit();
      }
    }

    from appium import webdriver
    from appium.options.android import UiAutomator2Options
    import requests
    import time
    
    options = UiAutomator2Options()
    options.device_name = 'Pixel 8'
    options.platform_version = '14.0'
    options.app = 'tb://your_app_id'
    options.set_capability('tb:options', {
        'key': 'api_key',
        'secret': 'api_secret',
        'name': 'Push Notification Test',
        'realDevice': True
    })
    
    driver = webdriver.Remote(
        command_executor='https://hub.testingbot.com/wd/hub',
        options=options)
    
    # Retrieve the FCM registration token from your app
    fcm_token = 'DEVICE_FCM_REGISTRATION_TOKEN'
    
    # Send a real push notification via FCM HTTP v1 API
    project_id = 'your-firebase-project-id'
    access_token = 'YOUR_OAUTH2_ACCESS_TOKEN'
    
    response = requests.post(
        f'https://fcm.googleapis.com/v1/projects/{project_id}/messages:send',
        headers={
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/json'
        },
        json={
            'message': {
                'token': fcm_token,
                'notification': {
                    'title': 'Order Update',
                    'body': 'Your order has been shipped!'
                }
            }
        }
    )
    
    time.sleep(3)
    driver.quit()

    const { remote } = require('webdriverio');
    
    const capabilities = {
      'platformName': 'Android',
      'appium:app': 'tb://your_app_id',
      'appium:deviceName': 'Pixel 8',
      'appium:platformVersion': '14.0',
      'appium:automationName': 'UiAutomator2',
      'tb:options': {
        'name': 'Push Notification Test',
        'realDevice': true
      }
    };
    
    const driver = await remote({
      hostname: 'hub.testingbot.com',
      port: 443,
      protocol: 'https',
      path: '/wd/hub',
      user: 'api_key',
      key: 'api_secret',
      capabilities
    });
    
    // Retrieve the FCM registration token from your app
    const fcmToken = 'DEVICE_FCM_REGISTRATION_TOKEN';
    
    // Send a real push notification via FCM HTTP v1 API
    const projectId = 'your-firebase-project-id';
    const accessToken = 'YOUR_OAUTH2_ACCESS_TOKEN';
    
    await fetch(
      `https://fcm.googleapis.com/v1/projects/${projectId}/messages:send`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          message: {
            token: fcmToken,
            notification: {
              title: 'Order Update',
              body: 'Your order has been shipped!'
            }
          }
        })
      }
    );
    
    await driver.pause(3000);
    await driver.deleteSession();

    require 'appium_lib'
    require 'net/http'
    require 'json'
    
    caps = {
      'platformName' => 'Android',
      'appium:app' => 'tb://your_app_id',
      'appium:deviceName' => 'Pixel 8',
      'appium:platformVersion' => '14.0',
      'appium:automationName' => 'UiAutomator2',
      'tb:options' => {
        'key' => 'api_key',
        'secret' => 'api_secret',
        'name' => 'Push Notification Test',
        'realDevice' => true
      }
    }
    
    driver = Appium::Driver.new({
      caps: caps,
      appium_lib: {
        server_url: 'https://hub.testingbot.com/wd/hub'
      }
    }).start_driver
    
    # Retrieve the FCM registration token from your app
    fcm_token = 'DEVICE_FCM_REGISTRATION_TOKEN'
    
    # Send a real push notification via FCM HTTP v1 API
    project_id = 'your-firebase-project-id'
    access_token = 'YOUR_OAUTH2_ACCESS_TOKEN'
    
    uri = URI("https://fcm.googleapis.com/v1/projects/#{project_id}/messages:send")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    
    request = Net::HTTP::Post.new(uri)
    request['Authorization'] = "Bearer #{access_token}"
    request['Content-Type'] = 'application/json'
    request.body = {
      message: {
        token: fcm_token,
        notification: {
          title: 'Order Update',
          body: 'Your order has been shipped!'
        }
      }
    }.to_json
    
    http.request(request)
    
    sleep 3
    driver.quit

    using OpenQA.Selenium.Appium;
    using OpenQA.Selenium.Appium.Android;
    using System.Text;
    using System.Text.Json;
    
    var options = new AppiumOptions();
    options.PlatformName = "Android";
    options.AddAdditionalAppiumOption("appium:app", "tb://your_app_id");
    options.AddAdditionalAppiumOption("appium:deviceName", "Pixel 8");
    options.AddAdditionalAppiumOption("appium:platformVersion", "14.0");
    options.AddAdditionalAppiumOption("appium:automationName", "UiAutomator2");
    
    var tbOptions = new Dictionary<string, object>
    {
        ["key"] = "api_key",
        ["secret"] = "api_secret",
        ["name"] = "Push Notification Test",
        ["realDevice"] = true
    };
    options.AddAdditionalAppiumOption("tb:options", tbOptions);
    
    var driver = new AndroidDriver(
        new Uri("https://hub.testingbot.com/wd/hub"), options);
    
    // Retrieve the FCM registration token from your app
    var fcmToken = "DEVICE_FCM_REGISTRATION_TOKEN";
    
    // Send a real push notification via FCM HTTP v1 API
    var projectId = "your-firebase-project-id";
    var accessToken = "YOUR_OAUTH2_ACCESS_TOKEN";
    
    var payload = JsonSerializer.Serialize(new {
        message = new {
            token = fcmToken,
            notification = new {
                title = "Order Update",
                body = "Your order has been shipped!"
            }
        }
    });
    
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");
    var content = new StringContent(payload, Encoding.UTF8, "application/json");
    await client.PostAsync(
        $"https://fcm.googleapis.com/v1/projects/{projectId}/messages:send",
        content);
    
    Thread.Sleep(3000);
    driver.Quit();

### Option 2: Quick test with ADB shell

For quick testing of notification _interaction_ (tapping, swiping away), you can post a system notification via ADB. This uses Appium's `mobile: shell` command.

Notifications created with `cmd notification post` are generic system notifications. They will **not** display your app's icon or appear as coming from your app. Use the [FCM approach](https://testingbot.com#android-fcm) above if you need app-branded notifications.

    // Post a generic system notification
    Map<String, Object> shellArgs = new HashMap<>();
    shellArgs.put("command", "cmd");
    shellArgs.put("args", new String[]{
        "notification", "post", "-S", "messaging",
        "-t", "Test Title", "Test Message"});
    driver.executeScript("mobile: shell", shellArgs);
    
    // Post a notification targeting a specific package (shows app icon on some Android versions)
    Map<String, Object> packageArgs = new HashMap<>();
    packageArgs.put("command", "cmd");
    packageArgs.put("args", new String[]{
        "notification", "post", "-S", "messaging",
        "--package", "com.example.myapp",
        "-t", "Order Update", "Your order has been shipped!"});
    driver.executeScript("mobile: shell", packageArgs);

To use `mobile: shell` on TestingBot, set the `adbExecTimeout` capability if your command takes longer than the default timeout.

## iOS Simulator Notifications

On iOS simulators, Appium provides the `mobile: pushNotification` command that accepts a JSON payload matching the Apple Push Notification format. This allows you to test how your app handles push notifications without needing a real APNs connection.

The `mobile: pushNotification` command only works on **iOS simulators** , not on real iOS devices. For real device testing, see the [iOS Real Devices](https://testingbot.com#ios-real) section.

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

    import io.appium.java_client.ios.IOSDriver;
    import io.appium.java_client.ios.options.XCUITestOptions;
    
    import java.net.URL;
    import java.util.HashMap;
    import java.util.Map;
    
    public class IOSPushNotificationTest {
      public static void main(String[] args) throws Exception {
        XCUITestOptions options = new XCUITestOptions();
        options.setDeviceName("iPhone 16");
        options.setPlatformVersion("18.0");
        options.setApp("tb://your_app_id");
    
        Map<String, Object> tbOptions = new HashMap<>();
        tbOptions.put("key", "api_key");
        tbOptions.put("secret", "api_secret");
        tbOptions.put("name", "iOS Push Notification Test");
        options.setCapability("tb:options", tbOptions);
    
        IOSDriver driver = new IOSDriver(
            new URL("https://hub.testingbot.com/wd/hub"), options);
    
        // Simulate a push notification
        Map<String, Object> payload = new HashMap<>();
        payload.put("bundleId", "com.example.myapp");
    
        Map<String, Object> alert = new HashMap<>();
        alert.put("title", "New Message");
        alert.put("body", "You have a new message from TestingBot");
        alert.put("subtitle", "Testing");
    
        Map<String, Object> aps = new HashMap<>();
        aps.put("alert", alert);
        aps.put("badge", 1);
        aps.put("sound", "default");
    
        Map<String, Object> payloadData = new HashMap<>();
        payloadData.put("aps", aps);
        payload.put("payload", payloadData);
    
        driver.executeScript("mobile: pushNotification", payload);
    
        Thread.sleep(2000);
        driver.quit();
      }
    }

    from appium import webdriver
    from appium.options.ios import XCUITestOptions
    import time
    
    options = XCUITestOptions()
    options.device_name = 'iPhone 16'
    options.platform_version = '18.0'
    options.app = 'tb://your_app_id'
    options.set_capability('tb:options', {
        'key': 'api_key',
        'secret': 'api_secret',
        'name': 'iOS Push Notification Test'
    })
    
    driver = webdriver.Remote(
        command_executor='https://hub.testingbot.com/wd/hub',
        options=options)
    
    # Simulate a push notification
    driver.execute_script('mobile: pushNotification', {
        'bundleId': 'com.example.myapp',
        'payload': {
            'aps': {
                'alert': {
                    'title': 'New Message',
                    'body': 'You have a new message from TestingBot',
                    'subtitle': 'Testing'
                },
                'badge': 1,
                'sound': 'default'
            }
        }
    })
    
    time.sleep(2)
    driver.quit()

    const { remote } = require('webdriverio');
    
    const capabilities = {
      'platformName': 'iOS',
      'appium:app': 'tb://your_app_id',
      'appium:deviceName': 'iPhone 16',
      'appium:platformVersion': '18.0',
      'appium:automationName': 'XCUITest',
      'tb:options': {
        'name': 'iOS Push Notification Test'
      }
    };
    
    const driver = await remote({
      hostname: 'hub.testingbot.com',
      port: 443,
      protocol: 'https',
      path: '/wd/hub',
      user: 'api_key',
      key: 'api_secret',
      capabilities
    });
    
    // Simulate a push notification
    await driver.execute('mobile: pushNotification', {
      bundleId: 'com.example.myapp',
      payload: {
        aps: {
          alert: {
            title: 'New Message',
            body: 'You have a new message from TestingBot',
            subtitle: 'Testing'
          },
          badge: 1,
          sound: 'default'
        }
      }
    });
    
    await driver.pause(2000);
    await driver.deleteSession();

    require 'appium_lib'
    
    caps = {
      'platformName' => 'iOS',
      'appium:app' => 'tb://your_app_id',
      'appium:deviceName' => 'iPhone 16',
      'appium:platformVersion' => '18.0',
      'appium:automationName' => 'XCUITest',
      'tb:options' => {
        'key' => 'api_key',
        'secret' => 'api_secret',
        'name' => 'iOS Push Notification Test'
      }
    }
    
    driver = Appium::Driver.new({
      caps: caps,
      appium_lib: {
        server_url: 'https://hub.testingbot.com/wd/hub'
      }
    }).start_driver
    
    # Simulate a push notification
    driver.execute_script('mobile: pushNotification', {
      bundleId: 'com.example.myapp',
      payload: {
        aps: {
          alert: {
            title: 'New Message',
            body: 'You have a new message from TestingBot',
            subtitle: 'Testing'
          },
          badge: 1,
          sound: 'default'
        }
      }
    })
    
    sleep 2
    driver.quit

    using OpenQA.Selenium.Appium;
    using OpenQA.Selenium.Appium.iOS;
    
    var options = new AppiumOptions();
    options.PlatformName = "iOS";
    options.AddAdditionalAppiumOption("appium:app", "tb://your_app_id");
    options.AddAdditionalAppiumOption("appium:deviceName", "iPhone 16");
    options.AddAdditionalAppiumOption("appium:platformVersion", "18.0");
    options.AddAdditionalAppiumOption("appium:automationName", "XCUITest");
    
    var tbOptions = new Dictionary<string, object>
    {
        ["key"] = "api_key",
        ["secret"] = "api_secret",
        ["name"] = "iOS Push Notification Test"
    };
    options.AddAdditionalAppiumOption("tb:options", tbOptions);
    
    var driver = new IOSDriver(
        new Uri("https://hub.testingbot.com/wd/hub"), options);
    
    // Simulate a push notification
    var payload = new Dictionary<string, object>
    {
        ["bundleId"] = "com.example.myapp",
        ["payload"] = new Dictionary<string, object>
        {
            ["aps"] = new Dictionary<string, object>
            {
                ["alert"] = new Dictionary<string, object>
                {
                    ["title"] = "New Message",
                    ["body"] = "You have a new message from TestingBot",
                    ["subtitle"] = "Testing"
                },
                ["badge"] = 1,
                ["sound"] = "default"
            }
        }
    };
    driver.ExecuteScript("mobile: pushNotification", payload);
    
    Thread.Sleep(2000);
    driver.Quit();

### Payload format

The `mobile: pushNotification` command accepts a payload that follows the [Apple Push Notification format](https://developer.apple.com/documentation/usernotifications/generating-a-remote-notification). The key fields are:

`bundleId`The bundle identifier of the target app (e.g. `com.example.myapp`).`payload.aps.alert.title`The title text shown in the notification banner.`payload.aps.alert.body`The main message text of the notification.`payload.aps.badge`The badge number to display on the app icon.`payload.aps.sound`The notification sound. Use `"default"` for the standard notification sound.

You can also include custom key-value pairs in the payload for your app to process:

    Map<String, Object> alert = new HashMap<>();
    alert.put("title", "Order Update");
    alert.put("body", "Your order #1234 has been shipped");
    
    Map<String, Object> aps = new HashMap<>();
    aps.put("alert", alert);
    
    Map<String, Object> payload = new HashMap<>();
    payload.put("aps", aps);
    payload.put("orderId", "1234");
    payload.put("deepLink", "/orders/1234");
    
    Map<String, Object> pushArgs = new HashMap<>();
    pushArgs.put("bundleId", "com.example.myapp");
    pushArgs.put("payload", payload);
    
    driver.executeScript("mobile: pushNotification", pushArgs);

### Wait for a notification with `mobile: expectNotification`

The XCUITest driver also provides [`mobile: expectNotification`](https://appium.github.io/appium-xcuitest-driver/latest/execute-methods/#mobile-expectnotification), which blocks test execution until a specific notification is delivered. This is useful when you want to verify that your app posts a particular notification in response to an action, without polling for UI elements.

`name` (required)The notification name to wait for (e.g. `com.example.orderComplete`).`type``"plain"` (default) for standard NSNotificationCenter notifications, or `"darwin"` for system-level Darwin notifications.`timeoutSeconds`Maximum time to wait in seconds. Defaults to 60. Throws a timeout error if the notification is not received.

    // Wait up to 10 seconds for a specific notification
    Map<String, Object> expectArgs = new HashMap<>();
    expectArgs.put("name", "com.example.myapp.orderComplete");
    expectArgs.put("timeoutSeconds", 10);
    driver.executeScript("mobile: expectNotification", expectArgs);

`mobile: expectNotification` listens for _internal_ notifications (NSNotificationCenter / Darwin), not push notification banners. Use it to verify that your app's code reacts correctly to events, alongside `mobile: pushNotification` which triggers the visible notification.

## iOS Real Device Notifications

Appium's `mobile: pushNotification` command does not work on real iOS devices because Apple only allows push notifications through APNs (Apple Push Notification service). To test push notifications on real iOS devices, you need to trigger them from your backend or via FCM/APNs.

### Disable app re-signing

When you upload an iOS app to TestingBot, it is automatically [re-signed with a TestingBot provisioning profile](https://testingbot.com/support/app-automate/help/app-resigning) so it can be installed on our devices. This re-signing **removes your app's push notification entitlements** , which means APNs will reject push tokens from the re-signed app and notifications will not be delivered.

To test push notifications on real iOS devices, you must **disable re-signing** by setting `resigningEnabled` to `false`. This requires your app to be signed with an [Apple Developer Enterprise certificate](https://developer.apple.com/programs/enterprise/), which allows installation on any device without TestingBot's provisioning profile.

    // Disable re-signing so push notification entitlements are preserved
    Map<String, Object> tbOptions = new HashMap<>();
    tbOptions.put("key", "api_key");
    tbOptions.put("secret", "api_secret");
    tbOptions.put("resigningEnabled", false);
    options.setCapability("tb:options", tbOptions);

Without `"resigningEnabled": false`, your app's push entitlements will be stripped during re-signing and push notifications will silently fail on real iOS devices.

### Trigger the notification

Once re-signing is disabled and your app retains its push entitlements, trigger notifications from your backend:

1. **Use your app's backend API** , If your app has an endpoint that triggers push notifications, call it from your test to send a real notification to the device under test. 
2. **Use Firebase Cloud Messaging (FCM)**, Send a push notification via the FCM API. Your app must be configured to receive FCM messages. 
3. **Use APNs directly** , Send a notification via the Apple Push Notification service using a valid APNs certificate or key. 

### Example: Trigger via your backend API
[Java](https://testingbot.com#)[Python](https://testingbot.com#)[NodeJS](https://testingbot.com#)[Ruby](https://testingbot.com#)

    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    
    // After your app is running and the device token is registered,
    // trigger a notification from your backend
    String json = "{\"device_token\": \"DEVICE_TOKEN_FROM_APP\","
        + "\"title\": \"New Message\","
        + "\"body\": \"You have a new message\"}";
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://your-api.example.com/send-notification"))
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(json))
        .build();
    
    HttpClient.newHttpClient().send(request,
        HttpResponse.BodyHandlers.ofString());
    
    // Wait for the notification to arrive
    Thread.sleep(3000);
    
    // Now interact with the notification in your Appium test
    // (see the "Interact with Notifications" section below)

    import requests
    import time
    
    # After your app is running and the device token is registered,
    # trigger a notification from your backend
    requests.post('https://your-api.example.com/send-notification', json={
        'device_token': 'DEVICE_TOKEN_FROM_APP',
        'title': 'New Message',
        'body': 'You have a new message'
    })
    
    # Wait for the notification to arrive
    time.sleep(3)
    
    # Now interact with the notification in your Appium test
    # (see the "Interact with Notifications" section below)

    // After your app is running and the device token is registered,
    // trigger a notification from your backend
    await fetch('https://your-api.example.com/send-notification', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        device_token: 'DEVICE_TOKEN_FROM_APP',
        title: 'New Message',
        body: 'You have a new message'
      })
    });
    
    // Wait for the notification to arrive
    await driver.pause(3000);
    
    // Now interact with the notification in your Appium test

    require 'net/http'
    require 'json'
    
    # After your app is running and the device token is registered,
    # trigger a notification from your backend
    uri = URI('https://your-api.example.com/send-notification')
    Net::HTTP.post(uri, {
      device_token: 'DEVICE_TOKEN_FROM_APP',
      title: 'New Message',
      body: 'You have a new message'
    }.to_json, 'Content-Type' => 'application/json')
    
    # Wait for the notification to arrive
    sleep 3
    
    # Now interact with the notification in your Appium test

## Interact with Notifications

After a notification is displayed, you can interact with it in your Appium test. The approach differs between Android and iOS.

### Android: Open the notification shade

On Android, use `openNotifications()` to pull down the notification shade, then find and tap the notification element.

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

    // Open the notification shade
    driver.openNotifications();
    
    // Wait for the notification to appear
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    WebElement notification = wait.until(ExpectedConditions
        .presenceOfElementLocated(By.xpath(
            "//*[contains(@text, 'Test Title')]")));
    
    // Tap the notification
    notification.click();

    from appium.webdriver.common.appiumby import AppiumBy
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    
    # Open the notification shade
    driver.open_notifications()
    
    # Wait for the notification to appear
    wait = WebDriverWait(driver, 10)
    notification = wait.until(EC.presence_of_element_located((
        AppiumBy.XPATH, "//*[contains(@text, 'Test Title')]"
    )))
    
    # Tap the notification
    notification.click()

    // Open the notification shade (WebdriverIO)
    await driver.execute('mobile: openNotifications');
    
    // Wait for the notification to appear
    const notification = await driver.$('//*[contains(@text, "Test Title")]');
    await notification.waitForExist({ timeout: 10000 });
    
    // Tap the notification
    await notification.click();

    # Open the notification shade
    driver.open_notifications
    
    # Wait for the notification to appear
    wait = Selenium::WebDriver::Wait.new(timeout: 10)
    notification = wait.until {
      driver.find_element(:xpath, "//*[contains(@text, 'Test Title')]")
    }
    
    # Tap the notification
    notification.click

    // Open the notification shade
    driver.OpenNotifications();
    
    // Wait for the notification to appear
    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    var notification = wait.Until(d =>
        d.FindElement(By.XPath("//*[contains(@text, 'Test Title')]")));
    
    // Tap the notification
    notification.Click();

### iOS: Tap the notification banner

On iOS, notifications appear as banners at the top of the screen. You can interact with them using the Notification Center or by tapping the banner directly.

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

    // Wait for the notification banner to appear
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    WebElement banner = wait.until(ExpectedConditions
        .presenceOfElementLocated(By.name("NotificationShortLookView")));
    
    // Tap the banner to open the notification
    banner.click();

    from appium.webdriver.common.appiumby import AppiumBy
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    
    # Wait for the notification banner to appear
    wait = WebDriverWait(driver, 10)
    banner = wait.until(EC.presence_of_element_located((
        AppiumBy.ACCESSIBILITY_ID, 'NotificationShortLookView'
    )))
    
    # Tap the banner to open the notification
    banner.click()

    // Wait for the notification banner to appear
    const banner = await driver.$('~NotificationShortLookView');
    await banner.waitForExist({ timeout: 10000 });
    
    // Tap the banner to open the notification
    await banner.click();

    # Wait for the notification banner to appear
    wait = Selenium::WebDriver::Wait.new(timeout: 10)
    banner = wait.until {
      driver.find_element(:accessibility_id, 'NotificationShortLookView')
    }
    
    # Tap the banner to open the notification
    banner.click

## Tips & Best Practices

### Grant notification permissions

Your app needs notification permissions before it can display push notifications. Use the `autoGrantPermissions` capability (Android) or `autoAcceptAlerts` capability (iOS) to automatically handle permission dialogs. See the [Permission Popups](https://testingbot.com/support/app-automate/appium/permission-popups) documentation for more details.

    // Android: auto-grant all permissions
    options.setCapability("appium:autoGrantPermissions", true);
    
    // iOS: auto-accept permission dialogs
    options.setCapability("appium:autoAcceptAlerts", true);

### Wait for notifications to arrive

Push notifications are asynchronous. Always use explicit waits instead of fixed delays when checking for notifications. Use `WebDriverWait` to poll for the notification element rather than `sleep()`. On iOS simulators, you can also use [`mobile: expectNotification`](https://testingbot.com#ios-expect) to block until an internal notification is delivered.

### Use unique notification content

When searching for a notification in the notification shade, use unique text content (such as a timestamp or test ID) to avoid matching stale or system notifications.

### Dismiss notifications after testing

After interacting with a notification, dismiss the notification shade (Android) or return to the app to continue your test flow.

    // Android: press Back to close the notification shade
    ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.BACK));
    
    // iOS: swipe up on the notification banner to dismiss
    Map<String, Object> swipeArgs = new HashMap<>();
    swipeArgs.put("direction", "up");
    driver.executeScript("mobile: swipe", swipeArgs);

## Frequently Asked Questions

### Can I test push notifications on real iOS devices?

Yes, but you cannot simulate them using Appium's `mobile: pushNotification` command, that only works on iOS simulators. For real iOS devices, you need to trigger actual push notifications through your backend, Firebase Cloud Messaging (FCM), or APNs (Apple Push Notification service). Your test can then interact with the notification banner or Notification Center using Appium's standard element interaction commands. See the [iOS Real Devices](https://testingbot.com#ios-real) section for examples.

### How do I simulate a push notification on Android with Appium?

On Android, use Appium's `mobile: shell` command with the ADB `cmd notification post` command. This sends a notification to the Android notification shade. Example: `driver.execute_script('mobile: shell', {'command': 'cmd', 'args': ['notification', 'post', '-S', 'messaging', '-t', 'Title', 'Message']})`. This works on both emulators and real Android devices on TestingBot.

### How do I open the notification shade in an Appium test?

On Android, use `driver.openNotifications()` (Java/Python/Ruby/C#) or `driver.execute('mobile: openNotifications')` (WebdriverIO). This pulls down the system notification shade. On iOS, notifications appear as banners at the top of the screen, wait for the banner element and tap it directly using its accessibility identifier.

### Why is my push notification not appearing during the test?

Common causes:

1. The app has not been granted notification permissions, use `autoGrantPermissions` (Android) or `autoAcceptAlerts` (iOS) to handle permission dialogs.
2. On iOS real devices, `mobile: pushNotification` only works on simulators, use your backend or FCM/APNs instead.
3. The notification may take a few seconds to appear, use explicit waits instead of fixed sleeps.

See the [Tips](https://testingbot.com#tips) section for more details.

Was this page helpful? Yes No 

## Looking for More Help?

Have questions or need more information?   
 You can reach us via the following channels:

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