Smart TV Testing

Run tests on physical Smart TV devices with TestingBot, using the open-source test framework Appium.

Currently TestingBot supports running automated tests on physical AppleTV 4k devices.

To get started with testing your Smart TV app in the cloud, you will need to follow these steps:

  • Upload your native Smart TV app, either to a public URL or using TestingBot Storage.
  • Create an Appium test script that will connect to TestingBot.
  • The results will be available in the TestingBot dashboard, together with a video of the test and logs.

Upload Smart TV App

To run Smart TV tests on TestingBot, you will first have to upload your .ipa file to TestingBot Storage.

$ curl -X POST "https://api.testingbot.com/v1/storage" \ -u key:secret -F "file=@/path/to/app/file/tvos.ipa"

This call will return a unique identifier for your SmartTV app ({"app_url": "tb://..."}), which you can use in the capabilities in your test (see example below).

More information regarding this API call and other similar calls (update uploaded file, delete uploaded file, list uploaded files) are available in the TestingBot API documentation.

Note: uploads to TestingBot Storage are automatically deleted after 62 days.

Run your first SmartTV test

Once you've uploaded your Smart TV app, you can start with running your first test on the TestingBot SmartTV cloud.

TestingBot currently only supports running tests on physical AppleTV 4k devices. Please see the example tests below, where we use the TestingBot tvOS example app to input two numbers in the calculator and verify its sum.

require 'rubygems' require 'appium_lib' caps = {} caps['name'] = 'Ruby tvOS Example' caps['deviceName'] = 'Apple TV 4K' caps['platformName'] = 'tvOS' caps['version'] = '17.4' caps['app'] = 'https://github.com/testingbot/tvos-example/releases/download/1.0.0/tvos-example.ipa' caps['realDevice'] = true 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 wait = Selenium::WebDriver::Wait.new(:timeout => 30) wait.until { driver.find_element(:accessibility_id, "inputField1").displayed? } inputField1 = driver.find_element(:accessibility_id, "inputField1") inputField1.click inputField1.send_keys 5 driver.execute_script('mobile: pressButton', { name: 'down' }) driver.execute_script('mobile: pressButton', { name: 'select' }) driver.switch_to.active_element.send_keys 10 driver.execute_script('mobile: pressButton', { name: 'down' }) driver.execute_script('mobile: pressButton', { name: 'select' }) result = driver.find_element(:accessibility_id, "resultField") if (!result.nil?) && (result.text.to_i == 15) puts "Test Passed" else puts "Test Failed" end driver.quit
const wdio = require('webdriverio'); const caps = { name: 'Node.js tvOS Example', deviceName: 'Apple TV 4K', platformName: 'tvOS', platformVersion: '17.4', app: 'https://github.com/testingbot/tvos-example/releases/download/1.0.0/tvos-example.ipa', realDevice: true }; const options = { protocol: 'https', hostname: 'hub.testingbot.com', port: 443, path: '/wd/hub', user: 'key', // Replace 'key' with your TestingBot key key: 'secret', // Replace 'secret' with your TestingBot secret capabilities: caps }; (async () => { // Start the Appium driver const driver = await wdio.remote(options); try { // Wait for the first input field to be displayed const inputField1 = await driver.$('~inputField1'); await inputField1.waitForDisplayed({ timeout: 30000 }); // Interact with the first input field await inputField1.click(); await inputField1.setValue(5); // Navigate down and select await driver.execute('mobile: pressButton', { name: 'down' }); await driver.execute('mobile: pressButton', { name: 'select' }); // Send keys to the active element await driver.switchToParentFrame(); // Ensure focus is on the active frame await driver.activeElement().setValue(10); // Navigate down and select the result await driver.execute('mobile: pressButton', { name: 'down' }); await driver.execute('mobile: pressButton', { name: 'select' }); // Verify the result const result = await driver.$('~resultField'); const resultText = await result.getText(); if (resultText && parseInt(resultText, 10) === 15) { console.log('Test Passed'); } else { console.log('Test Failed'); } } catch (err) { console.error('Error occurred:', err); } finally { // Quit the driver await driver.deleteSession(); } })();
from appium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # Define capabilities caps = { "name": "Python tvOS Example", "deviceName": "Apple TV 4K", "platformName": "tvOS", "platformVersion": "17.4", "app": "https://github.com/testingbot/tvos-example/releases/download/1.0.0/tvos-example.ipa", "realDevice": True } # Appium server URL server_url = "https://key:secret@hub.testingbot.com/wd/hub" # Initialize the driver driver = webdriver.Remote(server_url, caps) try: # Wait for the first input field to be displayed  wait = WebDriverWait(driver, 30) input_field1 = wait.until(EC.presence_of_element_located((By.ACCESSIBILITY_ID, "inputField1"))) # Interact with the first input field  input_field1.click() input_field1.send_keys("5") # Navigate down and select the second input field  driver.execute_script('mobile: pressButton', {'name': 'down'}) driver.execute_script('mobile: pressButton', {'name': 'select'}) # Send keys to the active element (second input field)  driver.switch_to.active_element.send_keys("10") # Navigate down and select the result field  driver.execute_script('mobile: pressButton', {'name': 'down'}) driver.execute_script('mobile: pressButton', {'name': 'select'}) # Verify the result  result_field = driver.find_element(By.ACCESSIBILITY_ID, "resultField") result_text = result_field.text if result_text and int(result_text) == 15: print("Test Passed") else: print("Test Failed") except Exception as e: print(f"An error occurred: {e}") finally: # Quit the driver  driver.quit()
import io.appium.java_client.AppiumDriver; import io.appium.java_client.MobileBy; import io.appium.java_client.remote.MobileCapabilityType; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; import java.net.URL; import java.time.Duration; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; public class TvOSTest { public static void main(String[] args) { AppiumDriver<WebElement> driver = null; try { // Set capabilities DesiredCapabilities caps = new DesiredCapabilities(); caps.setCapability("name", "Java tvOS Example"); caps.setCapability("deviceName", "Apple TV 4K"); caps.setCapability("platformName", "tvOS"); caps.setCapability("platformVersion", "17.4"); caps.setCapability("app", "https://github.com/testingbot/tvos-example/releases/download/1.0.0/tvos-example.ipa"); caps.setCapability("realDevice", true); URL serverUrl = new URL("https://key:secret@hub.testingbot.com/wd/hub"); driver = new AppiumDriver<>(serverUrl, caps); // Wait for the first input field to be displayed WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30)); WebElement inputField1 = wait.until(ExpectedConditions.presenceOfElementLocated(MobileBy.AccessibilityId("inputField1"))); inputField1.click(); inputField1.sendKeys("5"); // Navigate down and select the second input field driver.executeScript("mobile: pressButton", Map.of("name", "down")); driver.executeScript("mobile: pressButton", Map.of("name", "select")); // Send keys to the active element (second input field) WebElement activeElement = driver.switchTo().activeElement(); activeElement.sendKeys("10"); // Navigate down and select the result field driver.executeScript("mobile: pressButton", Map.of("name", "down")); driver.executeScript("mobile: pressButton", Map.of("name", "select")); WebElement resultField = driver.findElement(MobileBy.AccessibilityId("resultField")); String resultText = resultField.getText(); if (resultText != null && Integer.parseInt(resultText) == 15) { System.out.println("Test Passed"); } else { System.out.println("Test Failed"); } } catch (Exception e) { e.printStackTrace(); } finally { // Quit the driver if (driver != null) { driver.quit(); } } } }
using OpenQA.Selenium; using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.Enums; using OpenQA.Selenium.Appium.iOS; using OpenQA.Selenium.Support.UI; using System; using System.Collections.Generic; class TvOSTest { static void Main(string[] args) { AppiumDriver<IWebElement> driver = null; try { // Set capabilities var caps = new AppiumOptions(); caps.AddAdditionalCapability(MobileCapabilityType.PlatformName, "tvOS"); caps.AddAdditionalCapability(MobileCapabilityType.DeviceName, "Apple TV 4K"); caps.AddAdditionalCapability(MobileCapabilityType.PlatformVersion, "17.4"); caps.AddAdditionalCapability(MobileCapabilityType.App, "https://github.com/testingbot/tvos-example/releases/download/1.0.0/tvos-example.ipa"); caps.AddAdditionalCapability("realDevice", true); caps.AddAdditionalCapability("name", "C# tvOS Example"); // Initialize driver var serverUri = new Uri("https://key:secret@hub.testingbot.com/wd/hub"); driver = new IOSDriver<IWebElement>(serverUri, caps); // Wait for the first input field to be displayed var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30)); var inputField1 = wait.Until(d => d.FindElement(MobileBy.AccessibilityId("inputField1"))); // Interact with the first input field inputField1.Click(); inputField1.SendKeys("5"); // Navigate down and select the second input field driver.ExecuteScript("mobile: pressButton", new Dictionary<string, string> { { "name", "down" } }); driver.ExecuteScript("mobile: pressButton", new Dictionary<string, string> { { "name", "select" } }); // Send keys to the active element (second input field) var activeElement = driver.SwitchTo().ActiveElement(); activeElement.SendKeys("10"); // Navigate down and select the result field driver.ExecuteScript("mobile: pressButton", new Dictionary<string, string> { { "name", "down" } }); driver.ExecuteScript("mobile: pressButton", new Dictionary<string, string> { { "name", "select" } }); // Verify the result var resultField = driver.FindElement(MobileBy.AccessibilityId("resultField")); var resultText = resultField.Text; if (!string.IsNullOrEmpty(resultText) && int.Parse(resultText) == 15) { Console.WriteLine("Test Passed"); } else { Console.WriteLine("Test Failed"); } } catch (Exception ex) { Console.WriteLine($"An error occurred: {ex.Message}"); } finally { // Quit the driver driver?.Quit(); } } }

View SmartTV Test Results

Once your test has finished running, you will see a new test entry in the TestingBot dashboard.. If you click the test, you will see detailed information about the test, including a video recording of the test, logs and screenshots.

You can also use the TestingBot API to programmatically fetch the test results.

Was this page helpful?

Looking for More Help?

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