iOS native app automation testing
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()
}
...@implementation ExampleConfiguration
...
- (BOOL) allowInvalidSignatures {
#if DEBUG
return YES;
#else
return NO;
#endif
}
...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
}[self addUIInterruptionMonitorWithDescription: @"Appfigurate" handler: ^(XCUIElement *element) {
if (element.elementType == XCUIElementTypeAlert) {
if ([element.buttons[@"OK"] exists]) {
[element.buttons[@"OK"] tap];
return YES;
} else if ([element.buttons[@"Ignore"] exists]) {
[element.buttons[@"Ignore"] tap];
return YES;
}
}
return NO;
}];Create instance of APLConfiguration subclass
Get an instance of your
APLConfigurationsubclass in yourXCTestCase. A good place to do this in thesetUpmethod.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"ExampleConfiguration* c = (ExampleConfiguration*) [APLConfiguration sharedConfiguration];
c.boolean = NO;
c.string_Textfield = @"thursday";Apply configuration to XCUIApplication
Apply the result of
APLConfigurationautomationLaunchArgumentsmethod toXCUIApplication launchArgumentsproperty.Launch your app.
let app = XCUIApplication()
app.launchArguments = c.automationLaunchArguments()
app.launch()XCUIApplication* app = [XCUIApplication new];
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()
...- (void)testSendConfig {
app.launchArguments = [c automationLaunchArgumentsReset];
[app launch];
c.boolean = YES;
[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
XCTestCaseand 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
}App under test example
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
APLApplicationDidFinishLaunchingWithOptions(launchOptions);
#if DEBUG
APLAutomationMessageReceivedBlock(^id _Nullable(NSString * _Nonnull message, id _Nullable plist) {
UIWindow* window;
for (window in [UIApplication sharedApplication].windows) {
if ([window isKeyWindow]) {
break;
}
}
if ([message isEqualToString: @"SetDarkMode"]) {
window.overrideUserInterfaceStyle = [plist boolValue] ? UIUserInterfaceStyleDark : UIUserInterfaceStyleLight;
} else if ([message isEqualToString: @"GetDarkMode"]) {
return [NSNumber numberWithBool: window.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark];
}
return nil;
});
#endif
return YES;
}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)
}XCTestCase example
- (void)testSendMessageToApplicationUnderTest {
app.launchArguments = [config automationLaunchArgumentsReset];
[app launch];
APLAutomationSendMessage(@"SetDarkMode", @YES, 3.0);
XCTAssertTrue([APLAutomationSendMessage(@"GetDarkMode", nil, 3.0) boolValue]);
}The APLAutomationSendMessage and APLAutomationMessageReceivedBlock functions are compatible with both iOS Simulators and physical iOS devices.
Last updated