We've covered UI Tests afairamount over the years, but one thing has always stuck out: Running tests can be slow. Very slow.
Even if our individual tests themselves run quickly, the entire process is essentially "single threaded", slow to start up and complete each pass, and is prone to strange errors.
Today we'll take a look at Bluepill, a new tool from LinkedIn that can help us with all of this by running multiple iOS Simulators in parallel, simultaneously. Let's dive in.
We'll start by cloning the repository, then running the build script that comes inside:
./build.sh
This will build the command line tool. Once it's finsihed, we can copy the tool somewhere permanent:
cp build/Build/Products/Debug/bp /usr/local/bin
Then rename it to something we can more easily identify:
mv /usr/local/bin/bp /usr/local/bin/bluepill
(Note: /usr/local/bin is a common place to put command line tools on macOS thanks largely to the fact that Homebrew puts things there, so it's likely already in our $PATH).
Now that we have Bluepill installed, let's try it out.
We can head back to our project and run something like:
./bluepill -a ./Spaceships.app -s ./SpaceshipsUITests.xcscheme -o ./output/
This is great for quick runs, but ideally we'd be able to configure this sort of thing once and use it each time. Let's make a quick configuration file using JSON. We'll call it bluepill-config.json:
By default Bluepill will run 4 iOS Simulators simultaneously. Before we run our tests, let's turn that up a notch by adding one more option to our config file:
(This will cause our test to be run in up to 12 iOS Simulators at once. Very cool).
Not only are we saving tons of time this way, but Bluepill also does other helpful things for us, such as automatically retrying when the Simulator hangs or crashes. Neat.
We've only scratched the surface of what's possible with Bluepill. Be sure to check out the README for a full list of options and defaults.
Testing network requests can be tricky. Generating mock data, handling HTTP routes, etc. Thing get complicated quickly. Today we'll check out a library from devlucky called Kakapo that can help us tame all this. Let's get started.
One of the best features of Kakapo is how easy it is start using. We can create a new Router for a domain, and start adding intercepted routes like this:
In the above example, we're returning static data. Let's kick things up a notch and return some dynamic data. This is another place Kakapo really shines:
Code Coverage arrived with Xcode 7. It can help us visualize which parts of our code are not being tested enough (or at all). Let's dive in:
Before we can take advantage of Code Coverage, we'll need to enable it for our project. It's off by default.
We'll begin by editing our scheme, then selecting Test in the sidebar, then enabling the Gather coverage datacheckbox.
Now we'll run our tests by going to Product > Test in the menu (or pressing U). Then we can check out the test log where we'll find a new โCoverageโ tab.
Xcode will display all the functions in our code with a bar graph indicating how well โcoveredโ they are by our tests (We can mouse over to get an exact percentage). In our case, we are at 0%, since we don't have any tests. Yikes! Let's fix that by adding a simple test for a function on our PersonViewModel:
If we run our tests again, and check the Coverage tab, we can see we now have 50% test coverage for our tiny example project.
Last but not least, Xcode will also give us a heads up with a red shaded area in the right gutter on lines of code that aren't covered by our tests. Neat!
It's another fastlane friday here on LBOC. Today we'll be looking at another awesome tool in the fastlane suite called scan. It provides an easy way to run the tests of our iOS or OS X app. Let's dive in.
Before we begin, let's look at why a tool like scan can be helpful.
Xcode ships with a great command line tool called xcodebuild that allows to do all sorts of interesting things to our projects from the command line. It can be a bit verbose to configure though, and its output isn't very readable at a glance.
There's other tools like xcpretty that can help improve this output, but they take a fair amount of configuration as well.
That's where scan comes in. It takes care of all of this (plus a lot more) in one simple command: scan.
This is all we need for basics usage. scan will auto-detect things like our workspace, but we can always configure things as well:
scan --scheme "app-store"
Like other fastlane tools, we can run scan init to generate a new Scanfile, where we can store all our configuration options:
scheme"Spaceships"cleantrueoutput_types"html"
Other Features
๐ Displays nice output, stores original xcodebuild log in ~/Library/Logs/scan
๐ Can generate HTML, JSON or JUnit reports
๐ฃ Can send well-formatted test results to Slack. Check out the slack_only_on_failure configuration option to only report failed tests.
scan also helps with resolving common Xcode oddities like duplicated simulators or simulators that stop responding. Finally, scan works great with tools continuous integration tools like Jenkins and services like Travis. Happy testing!
Today we'll look at a couple more tips to get the most out of UI Testing in Xcode. (Covered previously in Bites #30, and #124).
Are We in a Test?
It'd be great if our app could know when it's running inside of a UI Test, then behave differently. We'll start by setting a flag that we can check for later. We'll add a launchArgument before we launch our app in our tests' setUpfunctions.
We can use this function throughout our code to do things like hit mock servers during testing, or simulate a camera preview when capturing screenshots with snapshot. (Bite #110).
Dismissing System Alerts
We have to jump through a couple of hoops here. First we'll need to set up what's called a "UI Interruption handler", which will execute a closure when the UI is interrupted by, for example, an alert being shown:
Then, after our app presents the authorization prompt, we'll need to .tap() on it once before the UI Interruption handler will fire. Inside our handler, we'll accept the prompt then return true to tell Xcode we've handled the UI interruption.
We covered the basics of UI Testing in Xcode when it was first announced all the way back in Bite #30. It's a fantastic way to test our app's interface. Today we'll look at a few more UI Testing odds and ends. Let's dive in:
First, let's write a test to verify that one of our table views properly loads and displays its data. The data is loaded asynchronously (which should be mocked in our test, but that's a future Bite). We'll need to give our app a little time to load and render the data we're testing for. We can use an expectation to easily pull this off:
Xcode will wait up to 5 seconds for the predicate we've passed in to be true (for there to be more than 0 cells in our table). After that the XCTAssert will be executed and evaluated.
We can change the simulated orientation of the device like this:
Testing is incredibly important in ensuring our app arrives to our users in as good a state as possible.
Xcode has had great unit testing support for a few releases now, but testing our UI has always been a challenge.
Instruments added support for it a while back, but itโs never felt like a first class citizen... Until now!
Xcode 7 brings us an awesome, fully baked set of UI testing tools. We can even make Xcode write much of your test code for us by simply interacting with our app.
Here's an example test created using the default Master-Detail template in Xcode. It presses the add button 3 times, then verifies that 3 cells now exist in the table view:
Recording UI Tests couldn't be easier. We just write an new empty test function, put our cursor in the middle of it, and press record.
UI Testing uses Accessibility under the hood to identify and retrieve UI elements at runtime. Just one more reason to make our apps as accessible as possible!