iOS Code Coverage Revisited
by Peter.
My earlier post on iOS code coverage reports was, if not wrong, at least sub-optimal. You still need to set Generate Test Coverage Files
and Instrument Program Flow
to Yes
for the Debug
build configuration.
Instead of manually flushing the coverage data upon finishing each test, it’s better to flush it have the entire testing. Likewise you don’t need to add any of your main source code files to the Compile Sources build phase of your test target. (All this is thanks to this Stack Overflow answer.)
You do this by adding a class that extends XCTestObserver
to the test target, e.g.:
@implementation GcovTestObserver - (void) stopObserving { [super stopObserving]; UIApplication* application = [UIApplication sharedApplication]; [application.delegate applicationWillTerminate:application]; } @end |
Then, in AppDelegate.m
add the following to - (void)applicationWillTerminate:(UIApplication *)aApplication
:
#ifdef DEBUG extern void __gcov_flush(void); __gcov_flush(); #endif |
And finally, add an initialize
method to AppDelegate
in order to add our GcovTestObserver
class as a test observer:
#ifdef DEBUG + (void)initialize { [[NSUserDefaults standardUserDefaults] setValue:@"XCTestLog,GcovTestObserver" forKey:@"XCTestObserverClass"]; } #endif |
Now coverage reports will be created for all the source code files in your project.
However, there’s one final issue: if you’re using the The Xcode CI service in OS X Server, the .gcda
files won’t be created. The Xcode CI service creates the directory where the coverage files are stored with the _teamsserver
user and 755
permissions. Since tests are run, and thus coverage files created, by the _xcstest
user, coverage files cannot be written to the directory. Because _xcstest
is a member of the _teamsserver
group, just making the directory group-writable fixes the problem.
To do this, add a Run Script build phase to the main target. The script is very simple:
chmod 775 ${OBJECT_FILE_DIR_normal}/i386 |
Update, 2013-12-30: A reader, Huangshuai, discovered that coverage reports will not include view or view controller classes that initialized in XIBs. The only solution seems to initialize the class in question using the traditional alloc init
dance.