Skip to main content

Command Palette

Search for a command to run...

Comprehensive Guide: Automating Android and Apple TV Apps with Appium

Published
11 min read
Comprehensive Guide: Automating Android and Apple TV Apps with Appium

The smart TV world underwent a major shake-up when Google introduced Android TV in 2014, followed by Apple's entry into the market with tvOS in 2015. Since then, we've seen an explosion of companies scrambling to get their apps onto these platforms, especially Android TV.

Now, if you've ever dabbled in mobile app development, you'll find some familiar ground with Android TV apps. But don't be fooled - there are plenty of unique quirks to wrap your head around. One thing's for sure, though: you can't skimp on quality assurance if you want your app to shine on the big screen.

With more and more folks cutting the cord and turning to streaming services, the need for rock-solid Android TV apps has never been greater. That's where automation testing comes in, and tools like Appium are becoming the go-to for many developers and testers.

Let's face it, if you're an automation tester looking to stay ahead of the curve, getting to grips with Android TV app testing is a smart move. As this technology continues to evolve, those who can navigate the intricacies of Android TV testing are likely to find themselves in high demand. After all, viewers today expect nothing less than a top-notch experience when they fire up their smart TVs.

At GeekyAnts, we’ve been at the forefront of this revolution, specializing in TV app automation since the early days of smart TV platforms. Our team of experts in Bangalore, India, has successfully automated testing for numerous high-profile TV apps, ensuring they perform flawlessly across different platforms.

How TV App Testing Differs from Mobile App Testing

  • Key difference: Remote control navigation vs touchscreen

  • Similar core testing process, but with platform-specific nuances

  • Design considerations: Landscape mode, video content focus

GeekyAnts’ extensive experience in both mobile and TV app development allows us to navigate these differences expertly, providing our clients with comprehensive testing solutions tailored to the unique requirements of TV platforms.

This blog serves as a detailed guide to automating TV app testing, from setting up your computer with your smart TV to automating essential tasks like searching and video playback.

1. Setting up the Environment

Before we begin, ensure you have the following installed:

  • Java Development Kit (JDK)

  • Android Studio (for Android TV)

  • Xcode (for Apple TV)

  • Node.js and npm

  • Appium Server

  • Appium Client (Java)

  • Maven (for dependency management)

To install Appium, run:

npm install -g appium

Add the following dependencies to your pom.xml:

ncies>
    <dependency>
        <groupId>io.appium</groupId>
        <artifactId>java-client</artifactId>
        <version>7.6.0</version>
    </dependency>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>3.141.59</version>
    </dependency>
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>7.4.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

GeekyAnts Tip: Our team has optimized this setup process, allowing us to quickly initialize new TV app testing projects for our clients. We ensure all dependencies are up-to-date and compatible, saving valuable development time.

2. Initializing Appium Driver

Here's how to initialize the Appium driver for Android TV and Apple TV:

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.net.URL;

public class TVAppAutomation {
    private AppiumDriver<MobileElement> driver;

    public void setupAndroidTV() throws Exception {
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability("platformName", "Android");
        caps.setCapability("deviceName", "Android TV");
        caps.setCapability("appPackage", "your.app.package");
        caps.setCapability("appActivity", "your.app.MainActivity");

        driver = new AndroidDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), caps);
    }

    public void setupAppleTV() throws Exception {
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability("platformName", "tvOS");
        caps.setCapability("deviceName", "Apple TV");
        caps.setCapability("bundleId", "your.app.bundleId");

        driver = new IOSDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), caps);
    }
}

GeekyAnts Insight: Our team has developed custom capabilities configurations that address common issues specific to TV app testing, ensuring smooth initialization across various TV models and OS versions.

3. Launching the App

To start automating your TV app tests, the first step is to launch the app you want to test on your Smart TV. Here’s how you can do it using Appium

The app will be launched automatically when initializing the driver. To relaunch:

public void launchApp() {
    driver.launchApp(); }

4. Navigating Using Remote

Since smart TVs lack touchscreens and rely on remote controls for user interaction, navigating apps with the remote becomes a key focus. Additionally, not all elements in the app have unique identifiers, so accessing them often requires using the remote control for navigation.

Controlling Android TV Devices (Fire TV/Nvidia Shield) with Keycodes

When working with Android TV devices like Amazon Fire TV Stick or Nvidia Shield, you can use Android keycodes to simulate remote control button presses. This is done using the pressKey method with the appropriate keycode.

For Android TV:

import io.appium.java_client.android.nativekey.AndroidKey;
import io.appium.java_client.android.nativekey.KeyEvent;

public void navigateRight() {
    ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_RIGHT));
}

public void navigateLeft() {
    ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_LEFT));
}

public void navigateUp() {
    ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_UP));
}

public void navigateDown() {
    ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_DOWN));
}

public void pressSelect() {
    ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_CENTER));
}

For Apple TV:

public void navigateRight() {
    driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "right"));
}

public void navigateLeft() {
    driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "left"));
}

public void navigateUp() {
    driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "up"));
}

public void navigateDown() {
    driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "down"));
}

public void pressSelect() {
    driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "select"));
}

Performing Remote Actions

For Android TV:

public void pressMenu() {
    ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.MENU));
}

public void pressBack() {
    ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.BACK));
}

For Apple TV:

public void pressMenu() {
    driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "menu"));
}


public void pressHome() {
    driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "home"));
}

Complete Android TV Keycode mapping

KeyCode

Button

KeyEvent

Behavior

4

Back

BACK

Returns the user to the previous operation or screen (Activity).

82

Menu

MENU

Opens the Android context menu (Options Menu).

23

Select (D-Pad Center)

DPAD_CENTER

Selects the UI item currently in focus.

19

Up (D-Pad)

DPAD_UP

Moves the focus upward in the UI.

20

Down (D-Pad)

DPAD_DOWN

Moves the focus downward in the UI.

21

Left (D-Pad)

DPAD_LEFT

Moves the focus to the left in the UI.

22

Right (D-Pad)

DPAD_RIGHT

Moves the focus to the right in the UI.

85

Play/Pause

MEDIA_PLAY_PAUSE

Toggles media playback between Play and Pause.

89

Rewind

MEDIA_REWIND

Rewinds or skips backwards in media playback contexts.

90

Fast Forward

MEDIA_FAST_FORWARD

Fast forwards or skips ahead in media playback contexts.

Understanding these key mappings is crucial for developers creating applications or automation for Android TV platforms. These keycodes provide a standardized way to interpret user input from various remote controls and input devices, ensuring consistent navigation and interaction across different Android TV apps and devices. 

By leveraging this keycode system, developers can create intuitive and responsive user interfaces that align with Android TV's design principles and user expectations.

Complete Apple TV Keycode Mapping

Button Name

Behavior

Home

Returns the user to the home screen (Activity).

Menu

Returns the user to the previous operation or screen (Activity).

Select

Selects the UI item currently in focus.

Up

Moves the focus upward in the UI.

Down

Moves the focus downward in the UI.

Left

Moves the focus to the left in the UI.

Right

Moves the focus to the right in the UI.

Play/Pause

Toggles media playback between Play and Pause.

Mastering these key mappings is essential for developers building applications or automations for Apple TV platforms. This standardized set of buttons and behaviors ensures a consistent user experience across various tvOS apps and devices.

By adhering to these established input conventions, developers can create intuitive and responsive interfaces that align with Apple TV's design philosophy and user expectations, resulting in seamless navigation and interaction for viewers.

5. Element Interactions

Locating Elements

import org.openqa.selenium.By;

public MobileElement findElementById(String id) {
    return driver.findElement(By.id(id));
}

public MobileElement findElementByAccessibilityId(String accessibilityId) {
    return driver.findElementByAccessibilityId(accessibilityId);
}

public MobileElement findElementByXPath(String xpath) {
    return driver.findElement(By.xpath(xpath));
}

Checking Element Focus

public boolean isElementFocused(MobileElement element) {
    return element.getAttribute("focused").equals("true");
}

Handling Non-Focusable Elements

public MobileElement findFocusedElementWithin(MobileElement container) {
    return container.findElement(By.xpath(".//*[@focused='true']"));
}

Navigating Grid Layouts

public MobileElement getElementInGrid(int row, int column) {
    String xpath = String.format("//android.widget.GridView/android.widget.LinearLayout[%d]/android.widget.LinearLayout[%d]", row, column);
    return driver.findElement(By.xpath(xpath));
}

Performing Search

public void performSearch(String searchTerm) {
    MobileElement searchField = findElementById("search_field");
    searchField.click();
    searchField.sendKeys(searchTerm);
    pressSelect();
}

Playing Video

public void playVideo(String videoTitle) {
    MobileElement video = driver.findElement(By.xpath(String.format("//android.widget.TextView[@text='%s']", videoTitle)));
    video.click();
    // Wait for video to start playing
    Thread.sleep(5000);
}

6. Complete Working Code Base

Here's a complete working code base that incorporates all the concepts we've discussed:

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.nativekey.AndroidKey;
import io.appium.java_client.android.nativekey.KeyEvent;
import io.appium.java_client.ios.IOSDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.net.URL;
import java.util.concurrent.TimeUnit;

public class TVAppAutomation {
    private AppiumDriver<MobileElement> driver;
    private boolean isAndroid;

    public void setup(boolean isAndroid) throws Exception {
        this.isAndroid = isAndroid;
        DesiredCapabilities caps = new DesiredCapabilities();
        
        if (isAndroid) {
            caps.setCapability("platformName", "Android");
            caps.setCapability("deviceName", "Android TV");
            caps.setCapability("appPackage", "your.app.package");
            caps.setCapability("appActivity", "your.app.MainActivity");
            driver = new AndroidDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), caps);
        } else {
            caps.setCapability("platformName", "tvOS");
            caps.setCapability("deviceName", "Apple TV");
            caps.setCapability("bundleId", "your.app.bundleId");
            driver = new IOSDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), caps);
        }
        
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    }

    public void launchApp() {
        driver.launchApp();
    }

    public void navigateRight() {
        if (isAndroid) {
            ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_RIGHT));
        } else {
            driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "right"));
        }
    }

    public void navigateLeft() {
        if (isAndroid) {
            ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_LEFT));
        } else {
            driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "left"));
        }
    }

    public void navigateUp() {
        if (isAndroid) {
            ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_UP));
        } else {
            driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "up"));
        }
    }

    public void navigateDown() {
        if (isAndroid) {
            ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_DOWN));
        } else {
            driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "down"));
        }
    }

    public void pressSelect() {
        if (isAndroid) {
            ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.DPAD_CENTER));
        } else {
            driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "select"));
        }
    }

    public void pressMenu() {
        if (isAndroid) {
            ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.MENU));
        } else {
            driver.executeScript("mobile: pressButton", ImmutableMap.of("name", "menu"));
        }
    }

    public MobileElement findElementById(String id) {
        return driver.findElement(By.id(id));
    }

    public MobileElement findElementByAccessibilityId(String accessibilityId) {
        return driver.findElementByAccessibilityId(accessibilityId);
    }

    public MobileElement findElementByXPath(String xpath) {
        return driver.findElement(By.xpath(xpath));
    }

    public boolean isElementFocused(MobileElement element) {
        return element.getAttribute("focused").equals("true");
    }

    public MobileElement findFocusedElementWithin(MobileElement container) {
        return container.findElement(By.xpath(".//*[@focused='true']"));
    }

    public MobileElement getElementInGrid(int row, int column) {
        String xpath = String.format("//android.widget.GridView/android.widget.LinearLayout[%d]/android.widget.LinearLayout[%d]", row, column);
        return driver.findElement(By.xpath(xpath));
    }

    public void performSearch(String searchTerm) {
        MobileElement searchField = findElementById("search_field");
        searchField.click();
        sendKeys(searchTerm);
        pressSelect();
    }

    public void playVideo(String videoTitle) throws InterruptedException {
        MobileElement video = driver.findElement(By.xpath(String.format("//android.widget.TextView[@text='%s']", videoTitle)));
        video.click();
        // Wait for video to start playing
        Thread.sleep(5000);
    }


    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    public static void main(String[] args) throws Exception {
        TVAppAutomation automation = new TVAppAutomation();
        
        // For Android TV
        automation.setup(true);
        automation.launchApp();
        automation.navigateRight();
        automation.navigateDown();
        automation.pressSelect();
        automation.performSearch("Nature Documentaries");
        automation.playVideo("Planet Earth II");
        automation.tearDown();

        // For Apple TV
        automation.setup(false);
        automation.launchApp();
        automation.navigateRight();
        automation.navigateDown();
        automation.pressSelect();
        automation.performSearch("Sci-Fi Movies");
        automation.playVideo("Inception");
        automation.tearDown();
    }
}

This code runs a complete test scenario on both Android TV and Apple TV, launching the app, navigating, performing a search, and playing a video on each platform.

GeekyAnts Expertise: This code base represents just a fraction of our comprehensive TV app testing framework. Our full suite includes advanced features like parallel test execution, detailed logging, and integration with CI/CD pipelines.

7. Conclusion

  • This guide provides a comprehensive overview of automating Android and Apple TV apps using Appium with Java

  • Covers environment setup, driver initialization, basic and advanced operations, and element interactions

  • Demonstrates how to create a complete working test scenario for both platforms

Key takeaways: - Automated testing ensures consistent quality across different TV platforms - Saves time and resources in the long run - Helps catch and fix issues before they reach end-users - Provides a framework for continuous testing as the app evolves

Why Choose GeekyAnts for Your TV App Automation Needs: - Years of specialized experience in TV app development and testing - A team of experts based in Bangalore, India, with a proven track record - Custom-built solutions that address TV-specific challenges - Comprehensive testing frameworks that go beyond basic automation - Commitment to staying ahead of the curve in the rapidly evolving smart TV landscape

At GeekyAnts, we do not just automate tests – we enhance the entire TV app development lifecycle. Let us bring our expertise to your project and ensure your TV app stands out in the competitive smart TV market.

15 views