Xcode Test Plans for iOS: Getting Started
In this tutorial, you’ll learn how to organize your unit and UI tests with Xcode test plans and how to test your iOS app with multiple languages/regions. By Irina Galata.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Xcode Test Plans for iOS: Getting Started
15 mins
- Getting Started
- Testing the Tests
- Unit Tests and UI Tests
- Creating Your First Test Plans
- Creating a Unit Test Plan
- Creating a Full Test Plan
- Testing Multiple Localizations
- Setting up Configurations
- Running the Localization Tests
- Adapting the Code for Locale-Dependent Tests
- Analyzing an App Alongside Testing
- Sanitizers
- Runtime API Checking
- Memory Management
- Where to Go From Here?
Setting up Configurations
If your app depends on the user’s region or language, it makes sense to run UI tests for a few languages and regions. Your budget app, with its use of currency symbols, is a good example of this.
Set up your FullTests plan to execute UI tests in both English and German by creating two separate configurations:
- Select FullTests.xctestplan. In the Configurations tab, rename Configuration 1 to German by pressing Enter and replacing the existing text.
- In the configuration settings, set the Application Language to German. Set the Application Region to Germany:
- Still in the Configurations tab, click + to add a new configuration.
- Select the newly-created configuration file and press Enter to rename it to English.
- In the English configuration, set the Application Language to English. Set the Application Region to United States.
Running the Localization Tests
Go to AccountsViewUITest and select Run in All Configurations. Do this by right-clicking the Play button to the left of AccountsViewUITest
:
Then, in the same menu, choose Jump to Report to review the test report:
Oh no, it failed! How can you fix it?
Start by looking at testUpdateBalance()
. Expand the report for the German configuration of that test to find the cause of the failure:
Click the Eye icon near the Automatic Screenshot text:
Apparently, you’re using the wrong currency symbol for the specified region: Germany. The currency appears in dollars, but it should be euros!
Adapting the Code for Locale-Dependent Tests
Go to AccountView.swift to fix the issue. You’ll find the computed property balanceString
there:
private var balanceString: String {
let amount = AmountFormatter.string(from: abs(account.balance)) ?? ""
let balance = "$" + amount
return account.balance < 0 ? "-" + balance : balance
}
The dollar sign is hard-coded! Hard coding, as you know, is rarely the solution, and this is no exception. :]
Update your code:
private var balanceString: String {
// 3
let amount = AmountFormatter.string(from: abs(account.balance)) ?? ""
let balance = currencySymbol + amount
return account.balance < 0 ? "-" + balance : balance
}
// 1
private var currencySymbol: String {
// 2
return Locale.current.currencySymbol ?? ""
}
Here's what you're doing in this code:
- You declare a new computed property,
currencySymbol
. - You get the currency symbol from the current user locale. That way, the currency symbol will be € for Germany and $ for the United States.
- Finally, you use the newly-declared property in
balanceString
instead of a hard-coded value.
Run the UI tests again and open the test report:
Oh! testAddAccount()
still fails!
Apparently, something went wrong with the translation of the navigation title. Look into Localizable.strings (German) and you'll see that there's a translation for the title New Account:
"BudgetKeeper" = "BudgetKeeper";
"New Account" = "Neues Konto";
"Update Balance" = "Kontostand updaten";
"OK" = "OK";
"Save" = "Speichern";
"Enter the name" = "Gib den Namen ein";
What else could be wrong then? Go to CreateAccountView.swift to investigate.
OK, you set the navigation title by calling navigationBarTitle(_:)
. So far, so good.
But the key is wrong! It should be New Account, not New account. Account has an uppercase A, not a lowercase a. Here's how you fix this:
.navigationBarTitle("New Account")
Run the tests once again to verify the correct localization:
Alles gut and way better! :]
Analyzing an App Alongside Testing
Xcode offers several tools to analyze your app as it runs, but many of them can't be used together or can affect performance. You also have to remember that the tools exist, and to turn them on (and off again), and to pay attention to the results. Good news! You can set up analysis tools as part of your test plans! Go to one of the configurations of your FullTests plan. Take a look at the settings on the bottom:
Sanitizers
The three sanitizers will check the following things in your app while it's running:
- Address Sanitizer (ASan) detects memory usage errors in your code. Turning on ASan can slow down your tests a bit and increase memory usage, but it's worth it to ensure your code is free of memory violations.
- Thread sanitizer (TSan) helps you detect data races, where multiple threads access and modify a memory area in an unsafe way. These are extremely difficult to find and debug, as they are quite unpredictable.
- Undefined Behavior Sanitizer (UBSan) detects issues like dividing by zero or accessing an array outside of its bounds.
Runtime API Checking
Main Thread Checker is on by default, and you should keep it this way for your tests. It will warn you if you use frameworks like UIKit or AppKit on a background thread. You should only update the UI in the main thread.
Memory Management
Malloc Scribble, Malloc Guard Edges and Guard Malloc help you detect usages of deallocated memory and buffer overruns. The latter means that your program overruns the bounds of a buffer while writing data into it. This results in overwriting storage adjacent to the buffer memory locations.
When the Zombie Objects setting is on, objects in your program won't be deallocated when they reach a retain count of 0. Instead, they'll stay half alive and half dead, just like zombies. You'll receive a warning when your program tries to access such an object.
Where to Go From Here?
Download the final project using the Download Materials button at the top or bottom of this tutorial.
Well done! You've now learned how to organize your tests and how to test in multiple localizations. This means there are no more excuses for not writing those tests!
If you want to deepen your knowledge of testing iOS apps, check out our awesome iOS Test-Driven Development by Tutorials book. You'll learn about new approaches, different types of tests, how to mock external services for testing and much more.
Also, to learn more about memory management tools in Xcode, take a look at Apple's Malloc Debugging documentation.
Thank you for reading along! If you have any questions or comments, don't hesitate to leave them below.