iOS: Setting Up Advanced Unit Testing

In the previous article iOS: Setting Up Basic Unit Testing I choose Google Toolbox for Mac as my unit testing framework. Now I will add OCMock and OCHamcrest.

OCMock: To create test doubles (mock, stubs etc.) in a unit test I like the objective-c mock framework OCMock. Its latest version adds support for iOS (universal (simulator, device) static lib). Take care to check the iOS instructions. You have to add a few compiler switches to avoid obscure linker errors.

Here is another nice article explaining what you can do with OCMock: Making Fun of Things with OCMock.

When I tried it for the first time the iOS support was only available in the svn repository and the linker stuff was not yet covered in the instructions. It is now, so we have one complication less :-)

To use the library I adjusted the “Header Seach Paths” & “Library Search Paths” in the “Search Path” section of the unit test target to include the OCMock paths (OCMock/Debug/Headers, OCMock/Debug, OCMock/Release/Headers, OCMock/Release). The Headers are the same for Debug and Release but currently OCMock creates the above layout and it will be easier to update if it is unchanged.

2011/2: I improved my setup: iOS: Setting Up Advanced Unit Testing Take Two.

CI Tip: I added the debug and release headers/library to my subversion project so I do not have to link to external files when I’m going to setup the Jenkins/Hudson build.

Unit Testing Tip: To get some insight why you should use test doubles watch this presentation by J.B. Rainsberger. Note that he is not really talking about integration tests, he talks about not writing integration tests to test units.  See also his blog where he admits that talking about “integration test” in this context is confusing. Anyway, there is something to learn in this video.

OCHamcrest: Another useful extension is OCHamcrest that allows for more declarative assertions. Nothing that is required but it makes the test more readable.

Complication No. 2, OCHamcrest does not provide iOS support out of the box.

2011/2: The issue described here has been fixed, it it no longer necessary to patch OCHamcrest. See also iOS: Setting Up Advanced Unit Testing Take Two.

Solution to Complication No. 2, There is a project on github that adds an iOS target to the build but it doesn’t create a similar universal library as OCMock. To make it similiar to OCMock I used the original Hamcrest objective-c source and added a static library target for iOS based on the OCMock xcode project.

I only used to the git project to fix the compile errors for the iOS target on the original sources. Luckily it required only two small changes:

Index: Source/Core/HCMatcherAssert.mm
===================================================================
--- Source/Core/HCMatcherAssert.mm	(revision 557)
+++ Source/Core/HCMatcherAssert.mm	(working copy)
@@ -%ld,%ld +%ld,%ld @@
#import "HCMatcher.h"

// Objective-C
-#import
+//#import
+#import

namespace {
Index: Source/Core/HCBaseMatcher.mm
===================================================================
--- Source/Core/HCBaseMatcher.mm	(revision 557)
+++ Source/Core/HCBaseMatcher.mm	(working copy)
@@ -%ld,%ld +%ld,%ld @@
- (void) subclassResponsibility:(SEL)command
{
[NSException raise:NSGenericException
-                format:@"-[%@  %s] not implemented", [self className], command];
+                format:@"-[%@  %s] not implemented", [self class/*Name*/], command];
}

@end

To use the library I adjusted the “Header Seach Paths” & “Library Search Paths” in the “Search Path” section of the unit test target to include the OCHamcrest paths as for OCMock (OCHamcrest/Debug/Headers, OCHamcrest/Debug, OCHamcrest/Release/Headers, OCHamcrest/Release).

Finally I added -lOCHamcrest and -lstdc++ (I added -ObjC and -all-load already for OCMock) to “Other Linker Flags” in the “Linking” Section. Adding HC_SHORTHAND to the “Preprocessor Macros” in the “GCC 4.2 Preprocessing” section allows me to assert without the “ST” prefix. (Based on the description from the github hamcrest)

// useless test example ;-)
- (void) testOCHamcrest {
 assertThatBool (YES, equalToBool (YES));
}


Complication No. 3: Unfortunately if an assertion fails the test runner crashes. This seems to be a bug in the simulator.

2011/2: The issue described here has been fixed, it it no longer necessary to patch OCHamcrest. See also iOS: Setting Up Advanced Unit Testing Take Two.

Solution to Complication No. 3: This can be fixed by patching Hamcrest with a patch provided by the Google Toolbox for Mac project. To apply the patch use something like this:

git apply -p 3 ~/Downloads/hamcrest_ios4_fix.patch

After applying the patch it works a lot better, but it is still not perfect. Hopefully the bug in the simulator will be fixed in a future version of the iOS SDK.

Summary: in this article I wrote about extending the unit testing tool set with OCMock and OCHamcrest.

Advice: if you are unfamiliar with OCMock check it out. You do not really need OCHamcrest if you don’t care about the declarative assertions. If you want to avoid Complication No. 2 and No 3. you can skip OCHamcrest. But I like it :-)

The next topic is iOS: Running Unit Tests with Code Coverage in Jenkins/Hudson.

This is the 3rd article of iOS: Setting Up a Test Environment is a Mess.

6 thoughts on “iOS: Setting Up Advanced Unit Testing

  1. iOS: Setting Up Basic Unit Testing « Software Noise

  2. iOS: Setting Up Advanced Unit Testing – Take Two « Software Noise

  3. iOS: Setting Up a Test Environment is a Mess « Software Noise

  4. iOS: Running Unit Tests with Code Coverage in Hudson « Software Noise

Leave a comment