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.
But wait….
Before I begin, I want to make one thing very clear, the difference between code unit testing and UItesting. 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)
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 (http://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 (http://gabriel.github.com/gh-unit/_examples.html)
#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
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)
18. Change theNSString *b = @"bar";
in the testFoo method toNSString *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).
转载:http://blog.xebia.com/2011/03/23/ios-xcode-4-ghunit-mobile-tddcontinuous-testing-part-2-of-n/