Blog

iOS + XCode 4 + GHUnit = Mobile TDD+Continuous testing part 2 of n

23 Mar, 2011

Last time I explained why I think doing TDD for mobile is imperative, and why I do it. But now it’s time to get technical, and explain to you how to set up, GHUnit in XCode 4 and run unit tests, not only in the iPhone and iPad simulator but also on your own physical device!, it’s in text and images but also in video form on YouTube.

Note, if you want to know why i chose GHUnit over OCUnit, just scroll down to the end of the post.

But wait….
Before I begin, I want to make one thing very clear, the difference between code unit testing and UI testing. Unfortunately, UI development can be hard to do in a TDD fashion. Especially when you want to test UI components. e.g. When I send a TouchEvent will the View respond and trigger my method in my controller.
My advice; don’t do UI testing with a Unit testing framework (OCUnit, JUnit, GHUnit), do it with for example the iOS automation API, which has been created specifically to test those UI components. I’ll also get back to you in a later post on how to do UI testing on Android.
What do you test with Unit testing frameworks? Well you test just the code, nothing more. Model and Controller code, not the View! You might need the help of a mocking framework to make it testable, because the view is missing and needs to be wired up for the controller and model to work properly, or it might need other controllers etc..
Let’s begin setting up!
With that in mind, let’s set up our own iPhone XCode 4 project! add GHunit, create a test and run it in the simulator or your own iOS device, iPhone, iPod Touch or iPad.
1. First of all download the GHUnitIOS version (0.4.28 at this time) at https://github.com/gabriel/gh-unit/archives/master
2. Unpack the downloaded zip file somewhere in your home directory, you should get the GHUnitIOS.framework directory

NOTE; I first placed it in /Developer/Library/Frameworks but XCode 4 didn’t like it and when compiling it could not find the header files, therefore I placed them somewhere in my home directory (e.g. /Users/rvanloghem/Development/Frameworks/GHUnitIOS.framework)


Right, you are now ready to set up your GHUnit ready XCode 4 project.
3. For example purposes, I’m choosing a normal, navigation-based application but you might have an existing project.
– NOTE; Don’t check the Include Unit Tests, because we are going to supply our own unit testing framework and not rely on OCUnit, which is supplied by default in XCode

4. In your XCode project settings (blue root icon in the tree browser) add a Target which you can call Tests, i usually base the project on a simple View-based Application (a Target named Tests will be added + a folder named Tests with all the Tests target files in them)

5. Next go back to the Tests target (click on the Tests Target) and add the GHUnitIOS.framework which you downloaded and unpacked in step 1 and 2. (click on the Build Phases tab, open up the Link Binary with Libraries, hit the + button, click Add Other and navigate+select the GHUnitIOS.framework directory on your filesystem)
6. Optional, but nice, move the GHUnitIOS.framework in your Tree to the Frameworks folder, to tidy things up

7. Set the -ObjC and -all_load in the other linker flags on the Tests Target (Select the Tests Target, select the Build Settings, search for other linker flags, and add the 2 flags)

8. Now you can delete some files which are not necessary, all the files in the Tests folder. (Note, Not! the Supporting Files folder as well, just the files!)
9. Delete the main.m file in the Supporting Files folder

10. In the Tests-Info.plist file (again, in the Supporting Files folder), clear out the Main nib file base name value

Time to create the GHUnit test runner, which will scan for our Unit Test Cases and run them.
11. Create an Objective-C class in the Tests folder named GHUnitIOSTestMain and make sure it is only! added to the Tests target

12. You can delete the GHUnitIOSTestMain.h file
13. Copy and paste source code from (https://github.com/gabriel/gh-unit/blob/master/Project-IPhone/GHUnitIOSTestMain.m) in your GHUnitIOSTestMain.m file
And now it’s time to create our own test case which will fail 😉
14. Again create an Objective-C class in the Tests folder and as this is an example, name it ExampleTest, also make sure it is added to the Tests Target only!
15. You can delete the ExampleTest.h file
15. Copy and paste the next piece of code which is 99% copy/pasted from the example code from GHUnit (https://gabriel.github.com/gh-unit/_examples.html)
[sourcecode language=”c”]// For iOS
#import <GHUnitIOS/GHUnit.h>
// For Mac OS X
//#import <GHUnit/GHUnit.h>
@interface ExampleTest : GHTestCase { }
@end
@implementation ExampleTest
– (BOOL)shouldRunOnMainThread {
// By default NO, but if you have a UI test or test dependent on running on the main thread return YES
return NO;
}
– (void)setUpClass {
// Run at start of all tests in the class
}
– (void)tearDownClass {
// Run at end of all tests in the class
}
– (void)setUp {
// Run before each test method
}
– (void)tearDown {
// Run after each test method
}
– (void)testFoo {
NSString *a = @"foo";
GHTestLog(@"I can log to the GHUnit test console: %@", a);
// Assert a is not NULL, with no custom error description
GHAssertNotNULL(a, nil);
// Assert equal objects, add custom error description
NSString *b = @"bar";
GHAssertEqualObjects(a, b, @"A custom error message. a should be equal to: %@.", b);
}
– (void)testBar {
GHAssertTrue(TRUE, @"Yes it worked");
}
@end
[/sourcecode]
Right, ready to run!
16. Launch your Tests target (iTunes-like play button arrow) and run it against the simulator-scheme and your Unit test app should start

17. Now in the app in the simulator hit the blue run button and your tests will execute. (and testFoo will fail!, you can click on it to see why it failed)

And now it’s time to fix it…
18. Change the NSString *b = @"bar"; in the testFoo method to NSString *b = @"foo";
19. Run the Test app again and re-run the test and they should be green or in this case black, which means your tests are ok
Showing the power of GHUnit
20. Run the app against your own iOS device-scheme. (Select iOS-device-scheme and click the iTunes like run button)

Why i chose GHUnit over OCUnit
IMHO, this shows the real power of the GHUnit testing framework, not only does it run in the simulator but it also runs on your own iPhone, iPad, etc. Whereas OCUnit can only run as part of your build on your own machine, not on your phone and not in the simulator, this for me, is a big dealbreaker.
The closer you can get your unit tests running against a real-world environment the better it will be. Why? Because you are making use of the real devices processor (not the intel x86), real memory management, or lack there-of, the real API’s etc. If my Unit tests run on my phone, i’m 99.999% certain that the code under test will actually run on, yes, you guessed it, my phone.
There is of-course a downside to GHUnit, OCUnit (bundled with XCode) can be run automatically prior to you compiling your own app, this makes getting feedback about regression a lot faster, GHUnit is something which you have to run manually, in this case. But to solve that problem, or at least make it a whole lot better, we can use continuous integration aka build server to do the auto-running-of-unit-tests for us. There is a very nice blog post which compares various iOS unit testing frameworks.
So what is next? Well, the topic for my next blog and video in this series is hooking up a XCode project + GHUnit to Jenkins (or Hudson for the Oracle minded people out there).

I'm always interested in the latest and greatest when it comes to; communication, infrastructure, user experience and coming up with some crazy creative solution which might seem as a weird combination ;) I use and spread the word about multimedia (podcasts, vodcasts, movies, comics) to effectively communicate concepts, ideas, documentation, past experiences and so on. Furthermore i am heavy into infrastructure but then the middleware part, like HTTP servers, Application Servers, Messaging, Virtualization, etc... I get really enthousiastic if the infrastructure is clustered, highly available and is critical to doing business! I also like to do development and thus "i eat my own dogfood".
guest
18 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Dave Crane
11 years ago

Excellent tutorial on GHUnit! Thank your very much.

Richard
Richard
11 years ago

Thanks!!! I was just looking for a unit-testing framework for Xcode!!

Mark
11 years ago

Robert,
Unfortunately I didn’t find your post before I wrote one last month discussing OCUnit vs. GHUnit. My conclusion was that as of Xcode 4, OCUnit has gotten good enough that I’m a fan – I’ll likely start using OCUnit.
My article did not mention the very clever point you’ve made, though — which is that OCUnit’s not going to run on the device. Well-taken, and I’d like to add that, along with attribution, to my article.
You may also be interested in the post I wrote about getting Jenkins to work with OCUnit & application tests (not only logic).
http://longweekendmobile.com/2011/04/15/unit-testing-in-xcode-4-use-ocunit-and-sentest-instead-of-ghunit/
Cheers
Mark

trackback

[…] yet, so it’s not really fair to judge. However, fellow iOS dev Robert has. See his article about setting up GHUnit with Xcode 4. He even has a sexy […]

Dominik Pich
Dominik Pich
11 years ago

Hi,
i incorporated GHUnit into Xcode XXbeta2 on osx XXgm and it works fine for iOS XXb2 and osx XXgm
I followed your great article
now Id like to proceed to make it run headless and in jenkins. (same as you)
I followed the docs and it does work ok for machos
But it fails for iOS XX
Any help / tip / hint?
Regards and thanks for an awesome article that helped me quite a lot
Dominik
—–
The console output follows:
BUILD xyKit-iOS OF PROJECT xyKit WITH CONFIGURATION Debug AND SDK x.0===
Check dependencies

PhaseScriptExecution “Run Script” build/xy.build/Debug-iphonesimulator/xy-iOSTests.build/Script-B4F8CF2A13C245C50000F3D6.sh
cd /Users/dominik/Desktop/preliminary_project_structure/Framework
/bin/sh -c /Users/dominik/Desktop/preliminary_project_structure/Framework/build/xyKit.build/Debug-iphonesimulator/xy-iOSTests.build/Script-B4F8CF2A13C245C50000F3D6.sh
Running: “/Users/dominik/Desktop/preliminary_project_structure/Framework/build/Debug-iphonesimulator/xy-iOSTests.app/xyKit-iOSTests” -RegisterForSystemEvents
2011-07-05 00:02:05.237 xyKit-iOSTests[370:ef03] Warning: CFFIXED_USER_HOME is not set! It should be set to the simulated home directory.
2011-07-05 00:02:05.274 xyKit-iOSTests[370:ef03] SBSetAccelerometerClientEventsEnabled failed: (ipc/send) invalid destination port

Aditi
Aditi
11 years ago

This is an excellent tutorial…i was struggling with GHUnit for about two weeks now… especially when the MAc and the Xcode got updated… it was almost like a boon to have found this tutorial! Thank you so much!

Ajay Rawat
Ajay Rawat
11 years ago

Thanks a lot buddy, Its help me a lot to configure my project with GHUnit.

Rutger van Dijk
Rutger van Dijk
10 years ago

Hi Robert,
Thanks for the tutorial, but unfortunately I can’t get it to work. First of all, I don’t have a file name ‘main.m’ in my Test folder.
Next, when I move the framework to the Framework folder, the specified framework turns red in the Test target ‘Link with libraries’.
Also, it seems that my Xcode 4 (4.1) seems to skip tests when I press ‘Test’ / ‘Run’.
To give you an example:
– (void)testExample
{
STFail(@”First test fails.”);
}
– (void)testSecondExample
{
NSString *myString = @”string”;
STAssertTrue([myString isEqualToString:@”string”], @”Second test fails.”);
}
The first one is run and fails. But the second one doesn’t run at all. According to the console log.
Any thoughts on this ? I would really like to create some unit tests, but at the moment Xcode is giving me more pain in the head than it should.
Thanks!
Rutger

rUpAs
rUpAs
10 years ago

Hi Robert,
Thank you very much for the beautiful article. The level of details you captured is very impressive.
But i faced few problems and need your help in solving the below problem.
I tried the same steps with xcode4.0 and xcode4.2. In both the places, after compiling everything and when i try to run as of step #17, the following error is observed
Undefined symbols for architecture i386:
“_main”, referenced from:
start in crt1.10.6.o
ld: symbol(s) not found for architecture i386
collect2: ld returned 1 exit status
Any clue what this means?

rUpAs
rUpAs
10 years ago

Actually the following link is not available to add code to the GHUnitIOSTestMain.m
https://github.com/gabriel/gh-unit/blob/master/Project-iOS/GHUnitIOSTestMain.m
Can you please make it available. This should solve my problem

Matti
Matti
10 years ago

Thanks for the tutorial!
By the way. Seems that something has been changed on XCode 4.2 and / or iOS 5 (could be my platform as well), and the docs didn’t work for me. It didn’t find any of the GHUnitIOS headers and the framework wasn’t in the list when trying to add it to the project link library list. So, I copied the GHUnitIOS.framework to /Developer/Library/Frameworks -directory. Now it shows in the framework list. What else I did, I replaced the UIApplicationMain initialization row with:
int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([GHUnitIOSAppDelegate class]));
and added:
#import
Now it starts without a crash.
Cheers,
Matti

Matti
Matti
10 years ago
Reply to  Matti

(For some reason the import row is broken, I imported GHUnitIOS/GHUnitIOSAppDelegate.h)

Rutger van Dijk
Rutger van Dijk
10 years ago

Hi!
Thanks for your reply: i fixed it!
Removed old target and cleanup the project (through the Organizer). Next, I followed your guide (and that of http://www.raywenderlich.com/3716/unit-testing-in-xcode-4-quick-start-guide)
Now it works! Thanks!

Vikram VI
Vikram VI
10 years ago

Hi Robert,
Thanks for this nice blog , last week I did setup of GHUnit with XCode 4.2.1 and could able to run sample test case.
But currently I’m facing a major roadblock as below , can you please help me out with it
1. Our application is written based on MVC model
2. I’m trying to call method written in this application but always getting error message as “Lexical or Preprocessor Issue ‘String’ file not found. I’m not able to overcome this compilation issue.”
It’ll be helpful if you can please share some real world application using GHUnit framework
Thanks in advance and eagerly waiting for your response.
Regards,
Vikram

Junda
10 years ago

One of the reason GHUnit is better than OCUnit is that it supports asynchronous methods or blocks!
I have also a guide on configuring GHUnit for CocoaPods that might be useful for devs: http://samwize.com/2012/10/04/how-to-setup-ghunit-with-cocoapods/

gsreddy
gsreddy
7 years ago

how to access/test application using GHUnit .please give me one example.
i was calling ..like that
MyTable *table=[[MyTable alloc]init];
[table method];
then i was run …then i have a error like that
symbol(s) not found for architecture i386

Explore related posts