Leveraging Argument Parser for UI Testing
August 16, 2020
In February of this year, Apple released a standalone package of their Argument Parser. This is a package that is used for command line tools. Argument Parser can also be used inside of your apps to help with automated testing. Consider the following scenario.
You are testing a very specific workflow within your app and you want to ensure state is the same for each test. You also want to do this as part of UI Testing, which is an arms-length activity (you don't get to set any flags through code). Historically, in order to achieve this, you would pass or set a DEBUG flag within your build settings and in your code.
#IF DEBUG
// do something debuggy work here
#ENDIF
Using Debug is an all-or-nothing type of path. This is where Argument Parser comes in. You are able to set specific flags and manage for them througout the code. I can now have a flag for debug, but also for forcing a database schema change or purging the databse on startup.
import ArgumentParser
struct Arguments: ParsableCommand {
@Flag(help:"Set app in debug mode")
var debug: Bool = false
@Flag(help:"Erase Database on Schema Change")
var eraseDatabaseOnSchemaChange: Bool = false
@Flag(help:"Purge database on startup")
var purge = false
}
extension Arguments {
static func configure() {
let arguments = Array(CommandLine.arguments.dropFirst())
if let parsed = try? Arguments.parseAsRoot(arguments),
let commands = parsed as? Arguments
{
// set variables as necessary from the commands
} else {
fatalError("Arguments not initialized")
}
}
}
With the use being something like the following:
if Services.arguments().purge {
do {
try self.purge()
} catch {
// manage your errors
}
}
UITesting has been around for a while and so has the ability to pass arguments. This the beautiful part of this work. I can now pass clean, clear arguments to my application to manage state any way that I need to. I could even provide nice documentation so that anyone else wanting to manage certain states can do so quickly for testing.
func testCreateBasicTask() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launchArguments = ["--debug", "--purge"]
app.launch()
// test all the things
}
What about when you just want to press "run" in xcode. Arguments are passed via the scheme so you can create a scheme for different states or you can simply change the scheme as necessary when you want to run a different state.
Some useful state management options that would be common
- skip login screen or any other UI State
- mock the network connection or just the data returned
- use the dev server for network connection instead of production
- set logging and metrics capture levels
- always a first time user