iOS native app automation testing

Appfigurate doesn't currently support automation testing watchOS, Flutter or React Native apps.

Appfigurate can change the configuration of an iOS app being automation tested using XCTestCase.

Examples

An example UI testing bundle AppfigurateExampleUITests is available in both Objective-C example and Swift example GitHub repositories. To test, ensure AppfigurateExample (ObjC/Swift iOS) is selected as the scheme, then long tap the run button to show more options and tap Test (⌘U).

Testing your app

Add APLConfiguration subclass to UI testing bundle

Tap on your APLConfiguration subclass in the project navigator. In File inspector ‣ Target membership, tick on your UI testing bundle.

Ensure allowInvalidSignatures returns YES/true

In your APLConfiguration subclass, confirm that your allowInvalidSignatures method returns YES/true when running automation tests. (Test schemes are by default run with a DEBUG build).

@objcMembers class ExampleConfiguration: APLConfiguration {
...
  override func allowInvalidSignatures() -> Bool {
    return !ENCRYPTED()
  }
...

Add an UIInterruptionMonitor

When the configuration is applied to the application on launch, the standard Appfigurate 'Configuration applied' alert is displayed. It is recommended you add an UIInterruptionMonitor to your XCTestCase to automatically dismiss this alert. A good place to do this in the setUp method.

addUIInterruptionMonitor(withDescription: "Appfigurate") { (element) -> Bool in
  if (element.elementType == .alert) {
    if (element.buttons["OK"].exists) {
      element.buttons["OK"].tap()
      return true
    } else if (element.buttons["Ignore"].exists) {
      element.buttons["Ignore"].tap()
      return true
    }
  }
  return false
}

Create instance of APLConfiguration subclass

  • Get an instance of your APLConfiguration subclass in your XCTestCase. A good place to do this in the setUp method.

  • Set the properties required to be applied to your app to allow it to be tested correctly.

let c = APLConfiguration.shared() as! ExampleConfiguration
c.boolean = false
c.string_Textfield = "thursday"

Apply configuration to XCUIApplication

let app = XCUIApplication()
app.launchArguments = c.automationLaunchArguments()
app.launch()

Once your app is launched, the configuration you set in Create instance of APLConfiguration subclass will be applied before executing each test case.

Additional automation launch methods

See also automationLaunchArgumentsReset and automationLaunchArgumentsWithAction methods of APLConfiguration.

Applying configuration at runtime to the app under test

You can apply configuration to the app after it has launched using the automationSendConfiguration, automationSendConfigurationReset and automationSendConfigurationWithAction methods of APLConfiguration. You can read the configuration from the app under test using the automationSendReadConfiguration method.

func testSendConfig() {
  app.launchArguments = c.automationLaunchArgumentsReset()
  app.launch()
  c.boolean = true
  c.automationSendConfiguration()
  ...

Invoking functionality in the app under test at runtime

Appfigurate allows you to invoke functionality in the app under test, at runtime, from your XCTestCase. Example use cases:

  • Setting mocked HTTP responses for HTTP requests in the app.

  • Share mocked objects between the XCTestCase and app.

  • Read and set the internal state of the app.

Instrument the app under test

Use the APLAutomationMessageReceivedBlock function in the app under test to declare a callback. The callback will be invoked anytime a message is received from the XCTestCase. Best practice is to wrap the function in #if DEBUG to ensure it's not included in an App Store build.

App under test example

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  APLApplicationDidFinishLaunchingWithOptions(launchOptions)
        
#if DEBUG
  APLAutomationMessageReceivedBlock { message, plist in
    let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first!
            
    if (message == "SetDarkMode") {
      let bool = plist as! Bool
      window.overrideUserInterfaceStyle = bool ? .dark : .light
    } else if message == "GetDarkMode" {
      let bool = window.traitCollection.userInterfaceStyle == .dark
      return bool
    }
    return nil
  }
#endif

  return true
}

Invoke functionality from your XCTestCase

Use the APLAutomationSendMessage function in your XCTestCase to send a message and wait for a reply from the app under test.

XCTestCase example

func testSendMessageToApplicationUnderTest() {
  app.launchArguments = c.automationLaunchArgumentsReset()
  app.launch()
  APLAutomationSendMessage("SetDarkMode", true, 3.0)
  XCTAssertTrue(APLAutomationSendMessage("GetDarkMode", nil, 3.0) as! Bool)
}

The APLAutomationSendMessage plist parameter accepts any object that is property list compatible, or nil. This also applies to the result returned from APLAutomationMessageReceivedBlock. The size of the property list once serialized, must be less than 65535 bytes, otherwise an AppfigurateLibraryException will be thrown from the XCTestCase. In practice, keep your property lists to just a few hundred bytes. Larger property lists will result in significant transmission time (tens of seconds).

Last updated