Code coverage… oy vey! Back in the days of running on iOS 6 using Xcode 4, measuring code coverage for unit tests was fairly straightforward. A set of coverage scripts I published made it easier still.
Then along came iOS 7 and Xcode 5. Coverage still worked if you continued to run on iOS 6. But on iOS 7, you’d get “ERROR: no .gcda files found” indicating that coverage data wasn’t being captured. “Well, maybe I need to switch from SenTestingKit to Apple’s new XCTest framework.” Nope, that didn’t help.
Call __gcov_flush() to write out code coverage data
But I want to call it just once, after all tests have finished.
Folks starting posting questions on the Apple Developer Forums. Apple’s answer was, “You have to call __gcov_flush() to collect coverage data with the iOS simulator.” There was no need to do this before because coverage data is also written when the app exits, which apparently used to happen at the conclusion of unit tests but “we have fixed that.”
Okay, so when do you call __gcov_flush()? The simplest way is to swizzle tearDown(). The problem with this that __gcov_flush() will be invoked after every test — for one of my projects, that’s over a thousand times! But I want to call it just once, after all tests have finished.
Test observers to the rescue
Dave MacLachlan, author of CoverStory, offers a better way, made available through Google Toolbox for Mac (GTM). SenTestingKit and XCTest both have mechanisms for test observers, which Dave puts to work. Here’s how to put his code into your tests:
- Get a copy of the latest GTM source code. You can get it straight from the official repository. I’ve also made a mirror on GitHub.
- Add UnitTesting/GTMCodeCoverageApp.h and .m to your application. Don’t put them in your test target, they belong in your application target.
If you use XCTest, add GTM_USING_XCTEST=1 to the “Preprocessor Macros” build setting for your application target.
- If you use SenTestingKit, add GTMCodeCoverageTestsST.m to your test target.
If you use XCTest, add GTMCodeCoverageTestsXC.m instead.
- Add GTM_IS_COVERAGE_BUILD=1 to “Preprocessor Macros” for your coverage configuration. Do this at the project level so that it goes into both the app target and the test target.
And voilà, your code coverage data is back. Thanks, Dave!