XCUITest an iOS app phone call

Background

Recently (2017-10) I talked with a an iOS developer.
In his company's app, the user is able to start a phone call.
Currently they test some of this function manually.

I decided to create an automated test.
Programmatically starting a call was easy, ending the call took more work!

XCUITest recorder

In Xcode I started the UI test recorder and manually operated the iPhone.
I started my app, made a phone call and ended it.
Unfortunately the recorded test didn't play to completion.
I edited the test script to reference "mobilephone.app", that didn't work.

I tried several alternative approaches before I found a simple solution via springboard app.

XCUITest with Springboard to tap End call button

Recently Apple added ability to XCUITest multiple apps.

Test Flow

XCUITest Code Snippet


let app = XCUIApplication()
let springboardApp = XCUIApplication(bundleIdentifier: "com.apple.springboard")

app.launch()

let appCallButton = app.buttons["Call Now"]
if appCallButton.waitForExistence(timeout: 10) {

    appCallButton.tap()

    springboardApp.buttons["Call"].tap()

    let delayBeforeEndCallSeconds: UInt32 = 12
    sleep(delayBeforeEndCallSeconds)

    springboardApp.buttons["End call"].tap()

Alternative approaches to end call

CXCallObserver

A test can use CXCallObserver delegate method callObserver(_ callObserver: callChanged:)
Then the test can verify that a call hasConnected or hasEnded.
But it's only an observer, doesn't have a method to end a call.

XCUITest Code Snippet

Set test expectations and delegate.


// make expectations properties so that delegate methods can reference them
weak var expectCallHasConnected: XCTestExpectation?
weak var expectCallHasEnded: XCTestExpectation?

func testCallTapped() {

    expectCallHasConnected = self.expectation(description: "expect call hasConnected")
    expectCallHasEnded = self.expectation(description: "expect call hasEnded")

    let callObserver = CXCallObserver()
    callObserver.setDelegate(self, queue: nil)

Then the test taps buttons to start and end call.

In callback, fulfill expectations.


extension PhoneyUITests: CXCallObserverDelegate {

    func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
        // This callback comes from iOS.
        // It accurately represents that a phone call connected or ended.
        if call.isOutgoing && call.hasConnected && !call.hasEnded {
            print("outgoing call hasConnected")
            if expectCallHasConnected != nil {
                expectCallHasConnected?.fulfill()
                // set nil to avoid error from calling multiple times
                // https://jeremywsherman.com/blog/2016/03/19/xctestexpectation-gotchas/#kaboom-calling-twice
                expectCallHasConnected = nil
            }
        }
        if call.isOutgoing && call.hasEnded {
            print("outgoing call hasEnded")
            if self.expectCallHasEnded != nil {
                expectCallHasEnded?.fulfill()
                // set nil to avoid error from calling multiple times
                expectCallHasEnded = nil
            }
        }
    }
}

CXProvider

I briefly tried using CXProvider in the test to end a call.
It didn't work, maybe because the call was started by the Phoney.app, not the test.

Accessibility Switch Control

This approach is very general, could be applied to many actions.
I may prototype it later.
For more info see Using a Raspberry Pi for iPhone Switch Control

Raspberry Pi and headphone breakout switch

A user can end a phone call by clicking the headphone switch.
A program can end a phone call by closing a headphone breakout switch.
I made a prototype.
For more info see Using a Raspberry Pi to end an iPhone phone call

Potential improvements

References

Phoney

Phoney is an iOS application to experiment with testing phone calls.
https://github.com/beepscore/Phoney

master branch

Uses XCUITest with Springboard to tap End call button.

request_web_service_end_call branch

Uses XCUITest to make an http request to an external web server, asks it to end call by closing switch.

XCUITest phone call with help from springboard

https://stackoverflow.com/questions/46322758/call-button-on-ui-testing

Build a universal translator (Raspberry Pi)

https://www.daveconroy.com/turn-raspberry-pi-translator-speech-recognition-playback-60-languages/

Speech Synthesis on the Raspberry Pi

https://learn.adafruit.com/speech-synthesis-on-the-raspberry-pi/introduction