Edition 66

Automating System Apps with Appium

Appium has long had the ability to temporarily close the current app under test and bring it back (the process of backgrounding an app), since testing the ability of an app to recover from being closed is important to app developers. When the XCUITest framework was released and we built the XCUITest driver for Appium, we discovered that once the app under test was backgrounded we could still launch other apps and automate them. This includes the built-in iOS apps like Messages, and the Settings app!

Initially, I thought that this was unintentional and that the capability would be removed, but it has stuck around for a couple years and some Appium features now depend on it.

While new to iOS, Android has long allowed testers to automate apps outside of the app under test.

If you noticed from the last couple articles about capturing network requests, we used the mobile: installCertificate command to install a custom SSL certificate. When given this command, Appium opens up the Settings app and navigates through menus, to install the certificate. This is achieved through the same automation Appium applies to your app, and you can easily do the same.

All it takes it launching an app using its bundleId, while your test is running:

// open Settings app
driver.activateApp("com.apple.Preferences");

This will launch the Settings app, and you can now inspect and automate as normal. To go back to the app under test, launch it the same way:

driver.activateApp("your.bundle.id");

If you don't know your app's bundleId there are numerous ways to get it. Appium needs it to automate your app in the first place, so I just took a look at the Appium logs for one of my test sessions and it included this line in the initial session setup:

[debug] [iOS] Getting bundle ID from app '/var/folders/mj/pgmgyh_95rsbs10yl46b73zc0000gn/T/2019319-8020-1rqx9az.mn5jj/TheApp.app': 'io.cloudgrey.the-app'

On Android, the process is the same, but you use the appPackage or appActivity instead of the bundleId.

That's it. I still don't expect this to work forever on iOS, but might as well use it while we have it. Your mileage may vary, depending on the app. The Settings app is constructed very simply, but more complex apps may exhibit strange behavior when attempting to automate outside the app under test.

The Settings apps have been the most useful for us to automate, anything a user can change about their phone, we can change too.

Another app you could automate would be the Messages app, with the bundleId com.apple.MobileSMS. You could use this for some tricky tests involving communication via SMS outside of your app, but if all you want to do is get a verification text, it may be better to use one of the many services which provide temporary phone numbers you can access programatically.

I found this list of the bundleIds of iOS version 12 system apps, this list of Android system apps. Additionally, we outlined how to find the package names of all apps on your Android device in (this earlier article about Android activities and intents)[https://appiumpro.com/editions/56].

Here's a quick example demonstrating the launch of system apps on iOS:

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.ios.IOSDriver;
import org.junit.After;
import org.junit.Test;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.net.MalformedURLException;
import java.net.URL;


public class Edition066_Automating_iOS_System_Apps {

    private String IOS_APP = "https://github.com/cloudgrey-io/the-app/releases/download/v1.9.0/TheApp-v1.9.0.app.zip";

    private AppiumDriver driver;

    @After
    public void Quit() {
        driver.quit();
    }

    @Test
    public void launchSystemApp() throws MalformedURLException, InterruptedException {
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability("platformName", "iOS");
        caps.setCapability("platformVersion", "12.2");
        caps.setCapability("deviceName", "iPhone Xs");
        caps.setCapability("automationName", "XCUITest");
        caps.setCapability("app", IOS_APP);

        driver = new IOSDriver(new URL("http://0.0.0.0:4723/wd/hub"), caps);

        WebElement picker = driver.findElementByAccessibilityId("Picker Demo");
        picker.click();

        // open iMessage
        driver.activateApp("com.apple.MobileSMS");

        // do things with SMS here

        Thread.sleep(3000);

        // open Settings app
        driver.activateApp("com.apple.Preferences");

        // regular automation commands to change device settings here

        // go back to our app
        driver.activateApp("io.cloudgrey.the-app");

        Thread.sleep(3000);
    }
}

That's all for this week. The example can be found with all our example code on Github.