iOS: Setting Up Advanced Unit Testing – Take Two

As Jon Reid mentioned on my “Setting Up Advanced Unit Testing” article on iOS testing, OCHamcrest now has  better support for iOS. It creates an OCHamcrest framework for IOS that includes the patches needed for iOS. I can now simply add the framework to my project and add a few additional linker options (which are the same as for OCMock) to make it work.

This should makes it a bit easier to setup hamcrest on iOS. Unfortunately there is a new complication. For iOS I now have to use

#import <OCHamcrestIOS/OCHamcrestIOS.h>

instead of

#import <OCHamcrest/OCHamcrest.h>

I don’t like that. I don’t see why it has to use a different name.

My solution is to build my own static lib for OCHamcrest. That is quite easy and it is my recommended solution.

Initially I switched OCMock to a static library because I had some issue compiling OCMock with block support. It didn’t pick up the proper iOS version and I always ended up with a lib missing block support. So finally I copied all the OCMock source files to my project and created a static library.

The OCMock source now live inside my project in a folder called “Source Xternal/OCMock”. To create a static lib I added a new “static library” target to my project (in XCode, right click Targets and Add/New Target and then select static library) with the OCMock source files below “Source Xternal/OCMock”.  Next I added the new lib (libOCMock.a) to my unit test project by adding it to the “Linked Libraries” (Get Info/Linked Libraries on the unit test target). The last step was to add “Source Xternal” to the “Header Search Paths” in the build settings.

Since the source folder is named OCMock the usual #import will work without change, i.e.

#import <OCMock/OCMock.h>

I didn’t need to modify any #import statement. (Now it also picks up the proper settings to compile it with block support :-)).

I did the same with OCHamcrest. The only difference to the procedure used for OCMock was that I flattened the sources of OCHamcrest into a single folder “Source Xternal/OCHamcrest”. Now all header files are below OCHamcrest and I do not have to touch the hamcrest #imports. (The original source layout uses subfolder to group the files).

This also removed the unwanted “IOS” postfix. So I can simply use the common #import <OCHamcrest/OCHamcrest.h>.

Finally I also moved Google Toolbox for Mac stuff to my “Source Xternal” folder into “Source Xternal/GoogleToolbox” and created another static lib for it.

So my source layout now looks like this:

Source
...
Source Test
...
Source Xternal
Source Xternal/OCMock
Source Xternal/OCHamcrest
Source Xternal/GoogleToolbox

That’s straight forward and also improves the build on my continuous integration server as it now builds all my dependencies as part of my project with the same compiler settings.

Another nice side effect is, that the source of the libraries is not far away and I can easily look into it. From time to time I get strange error messages when compiling and sometimes it helps to take a look into the source of OCHamcrest.

I’m already using this layout for a while now and its definitely my recommendation!

Happy Testing!

Advertisements

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.

iOS: Setting Up a Test Environment is a Mess

Really. I have tried. I have done it successfully but it was a bit of work.

There are a number of complications to set up a decent testing environment for iOS. I will describe which tools I choose and what I had to do to get everything running. I will not describe everything in detail but hopefully it will still give you a starting point.

All in all, I think that this should be (a lot) easier, even in this part of the C universe.

So what do I consider a decent testing environment?

I think at first we need support for unit tests and acceptance tests (in the agile meaning). Unit tests to get the low level stuff right (we can also use it for some integration testing) and acceptance tests to do some end-to-end testing.

Running all the tests manually is not a good idea anymore so I will run them on a continuous integration server, which will also take care of collecting code coverage.

My current collection of tools include XCode (xcodebuild, gcc, gcov) with iOS SDK, Google Toolbox for MacOCMock, OCHamcrest, Cucumber, Frank (includes UISpec) and Jenkins/Hudson (with gcovr for xml-ifying gcov).

All tools are free and all I had to do was to make them play together. A number of blogs provided helpful information. I will try to list them for reference.

There is a lot of stuff and to make it an easier read I split in into multiple articles. Here is the complete list:

February 2011: Here is new article regarding setup of OCMock & OCHamcrest:

Ok, let’s start with iOS: Setting Up Basic Unit Testing.