Introduction
Unit testing is a standard technique in computer programming whereby we break the software into small, independent pieces of code (a.k.a. the unit), isolate it from the rest of the code and test it. The main goal of unit testing is to try and find as many bugs as possible, as soon as possible.
Unit testing will be a large part of your work in the Software Development Project (and of your software engineer career!). Neglecting it will nearly always be a huge mistake!
We distinguish to kinds of testing :
- Logit Unit Testing
Logic unit testing concerns the testing of the classes logic directly. You only test the logic of the methods, you don't interact with user interface elements or simulate them. For example, testing that a coordinate convertor utility is returning correct values is logic unit testing. - Application Unit Testing
Application testing in iOS is much more specific to applications with user interface. In application testing, you will check that clicking a certain button triggers the correct behavior, that a view loads correctly, and so on.
In this tutorial, you will learn how to logic test and application test your iOS applications in Xcode using the integrated tools.
A full tutorial on Unit testing can be found on Apple website :
Requirements
Example of Logic Unit Testing



Build Settings

$(BUILT_PRODUCTS_DIR)/MyLocation.app/MyLocation

$(BUNDLE_LOADER)

MyLocationLogicTests.m
#import "MyLocationLogicTests.h"
#import "Calculator.h"
@implementation MyLocationLogicTests
- (void)setUp
{
[super setUp];
// Set-up code here.
}
- (void)tearDown
{
// Tear-down code here.
[super tearDown];
}
- (void)testAddition {
NSString* test = [Calculator computeWithOperand:@"+" number1:@"2" andNumber2:@"3"];
NSString* realResult = [NSString stringWithFormat:@"%lf", 5.0];
STAssertTrue([test isEqualToString:realResult], @"");
}
- (void)testSubstraction {
NSString* test = [Calculator computeWithOperand:@"-" number1:@"2" andNumber2:@"3"];
NSString* realResult = [NSString stringWithFormat:@"%lf", -1.0];
STAssertTrue([test isEqualToString:realResult], test);
}
- (void)testProduct {
NSString* test = [Calculator computeWithOperand:@"*" number1:@"2" andNumber2:@"3"];
NSString* realResult = [NSString stringWithFormat:@"%lf", 6.0];
STAssertTrue([test isEqualToString:realResult], @"");
}
- (void)testDivision {
NSString* test = [Calculator computeWithOperand:@"/" number1:@"6" andNumber2:@"3"];
NSString* realResult = [NSString stringWithFormat:@"%lf", 2.0];
STAssertTrue([test isEqualToString:realResult], @"");
STAssertThrows([Calculator computeWithOperand:@"/" number1:@"6" andNumber2:@"0"], @"");
}
- (void)testBadInputs {
// must throw an exception to pass the test
STAssertThrows([Calculator computeWithOperand:@"/+" number1:@"6" andNumber2:@"0"], @"");
STAssertThrows([Calculator computeWithOperand:@"" number1:@"6" andNumber2:@"0"], @"");
STAssertThrows([Calculator computeWithOperand:@"a" number1:@"6" andNumber2:@"0"], @"");
STAssertThrows([Calculator computeWithOperand:@"ab" number1:@"6" andNumber2:@"0"], @"");
STAssertThrows([Calculator computeWithOperand:@"+" number1:@"a" andNumber2:@"0"], @"");
STAssertThrows([Calculator computeWithOperand:@"+" number1:@"5" andNumber2:@"a"], @"");
}
@end



Test Suite '/Users/<gaspar>/Library/Developer/Xcode/DerivedData/MyLocation-aigvggguqsokvbbpxwtxqdyqmhzp/Build/Products/Debug-iphonesimulator/MyLocationLogicTests.octest(Tests)' started at 2011-10-30 19:42:28 +0000
Test Suite 'MyLocationLogicTests' started at 2011-10-30 19:42:28 +0000
Test Case '-[MyLocationLogicTests testAddition]' started.
Test Case '-[MyLocationLogicTests testAddition]' passed (0.000 seconds).
Test Case '-[MyLocationLogicTests testBadInputs]' started.
Test Case '-[MyLocationLogicTests testBadInputs]' passed (0.000 seconds).
Test Case '-[MyLocationLogicTests testDivision]' started.
Test Case '-[MyLocationLogicTests testDivision]' passed (0.000 seconds).
Test Case '-[MyLocationLogicTests testProduct]' started.
Test Case '-[MyLocationLogicTests testProduct]' passed (0.000 seconds).
Test Case '-[MyLocationLogicTests testSubstraction]' started.
Test Case '-[MyLocationLogicTests testSubstraction]' passed (0.000 seconds).
Test Suite 'MyLocationLogicTests' finished at 2011-10-30 19:42:28 +0000.
Executed 5 tests, with 0 failures (0 unexpected) in 0.001 (0.001) seconds
Test Suite '/Users/<gaspar>/Library/Developer/Xcode/DerivedData/MyLocation-aigvggguqsokvbbpxwtxqdyqmhzp/Build/Products/Debug-iphonesimulator/MyLocationLogicTests.octest(Tests)' finished at 2011-10-30 19:42:28 +0000.
Executed 5 tests, with 0 failures (0 unexpected) in 0.001 (0.002) seconds
Example of Application Unit Testing

#import <SenTestingKit/SenTestingKit.h>
#import "MyLocationAppDelegate.h"
#import "NameViewController.h"
#import "MapViewController.h"
@interface MyLocationApplicationTests : SenTestCase {
MyLocationAppDelegate* appDelegate;
UINavigationController* mainNavigationController;
NameViewController* nameViewController;
MapViewController* mapViewController;
}
@end
You can see we need a pointer to the application delegate class itself. We will indeed check that the application starts correctly.
Now, implement MyLocationApplicationTests.m with the following :
MyLocationApplicationTests.m
#import "MyLocationApplicationTests.h"
@implementation MyLocationApplicationTests
- (void)setUp {
[super setUp];
// Set-up code here.
appDelegate = [[[UIApplication sharedApplication] delegate] retain];
mainNavigationController = (UINavigationController*)appDelegate.window.rootViewController;
nameViewController = (NameViewController*)mainNavigationController.visibleViewController;
}
- (void)tearDown {
// Tear-down code here.
[appDelegate release];
[super tearDown];
}
- (void)testApplicationDelegate {
STAssertTrue([appDelegate isMemberOfClass:[MyLocationAppDelegate class]], @"bad UIApplication delegate");
STAssertTrue([mainNavigationController isMemberOfClass:[UINavigationController class]], @"bad mainViewController");
}
- (void)testNameViewController {
STAssertTrue([nameViewController isMemberOfClass:[NameViewController class]], @"");
UIButton* nextButton = (UIButton*)[nameViewController.view viewWithTag:1];
[nextButton sendActionsForControlEvents:(UIControlEventTouchUpInside)];
STAssertTrue(mainNavigationController.visibleViewController == nameViewController, @"empty name check did not work"); //should not go to next view as username textfield is empty
UITextField* nameTextField = (UITextField*)[nameViewController.view viewWithTag:2];
nameTextField.text = @"Toto";
[nextButton sendActionsForControlEvents:(UIControlEventTouchUpInside)];
STAssertTrue([mainNavigationController.visibleViewController isMemberOfClass:[MapViewController class]], @"mapViewController not coming when touching next button");
}
@end



Test Suite 'All tests' started at 2011-11-13 21:32:12 +0000
Test Suite '/Users/<gaspar>/Library/Developer/Xcode/DerivedData/MyLocation-glwkfxnrtujdosesedhjfujyjnxy/Build/Products/Debug-iphonesimulator/MyLocationApplicationTests.octest(Tests)' started at 2011-11-13 21:32:12 +0000
Test Suite 'MyLocationApplicationTests' started at 2011-11-13 21:32:12 +0000
Test Case '-[MyLocationApplicationTests testApplicationDelegate]' started.
Test Case '-[MyLocationApplicationTests testApplicationDelegate]' passed (0.000 seconds).
Test Case '-[MyLocationApplicationTests testNameViewController]' started.
Test Case '-[MyLocationApplicationTests testNameViewController]' passed (0.150 seconds).
Test Suite 'MyLocationApplicationTests' finished at 2011-11-13 21:32:13 +0000.
Executed 2 tests, with 0 failures (0 unexpected) in 0.150 (0.150) seconds
Test Suite '/Users/<gaspar>/Library/Developer/Xcode/DerivedData/MyLocation-glwkfxnrtujdosesedhjfujyjnxy/Build/Products/Debug-iphonesimulator/MyLocationApplicationTests.octest(Tests)' finished at 2011-11-13 21:32:13 +0000.
Executed 2 tests, with 0 failures (0 unexpected) in 0.150 (0.150) seconds
Test Suite 'All tests' finished at 2011-11-13 21:32:13 +0000.
Executed 2 tests, with 0 failures (0 unexpected) in 0.150 (0.151) seconds