Fortunately there is no shortage of quality testing frameworks for iOS. I decided to focus my efforts on 3 of the available frameworks:
OCUnit
Based on the SenTest framework, OCUnit is a traditional xUnit style unit testing tool that comes bundled with the iOS SDK.Kiwi
An RSpec style testing tool with an extensible expectation syntax and built-in mocking tools for Behavior-Driven Development (BDD).KIF
A fully automated functional testing tool that simulates user interactions by leveraging accessibility attributes provided by the OS.Three the Hard Way (HelloTDDiOS)
Based on Your first iOS App tutorial on Apple Developer, Three the Hard Way allows you to enter a name via text input, click a button, and view the resulting greeting.
Development of the app was 100% test-driven using OCUnit. Below is an example of tests written to exercise the button callback in the view controller.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@implementation HelloTDDViewControllerTests { | |
HelloTDDViewController *viewController; | |
} | |
- (void)setUp { | |
viewController = [[HelloTDDViewController alloc] init]; | |
} | |
//... | |
- (void)testOnButtonClickNotifiesDelegate { | |
MockHelloTDDViewControllerDelegate *delegate = | |
[[MockHelloTDDViewControllerDelegate alloc] init]; | |
viewController.delegate = delegate; | |
[viewController onButtonClick:nil]; | |
BOOL result = delegate.buttonWasClicked; | |
STAssertTrue(result, | |
@"View controller should notify delegate on button click."); | |
} | |
- (void)testOnButtonClickPassesNameToDelegate { | |
MockHelloTDDViewControllerDelegate *delegate = | |
[[MockHelloTDDViewControllerDelegate alloc] init]; | |
viewController.delegate = delegate; | |
UITextField *textField = [[UITextField alloc] init]; | |
textField.text = @"First Last"; | |
viewController.nameField = textField; | |
[viewController onButtonClick:nil]; | |
STAssertEqualObjects(delegate.lastNameSent, @"First Last", | |
@"View controller should pass name to delegate."); | |
} | |
//... | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@implementation HelloTDDViewController | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
GreetingFactory *greetingFactory = [[GreetingFactory alloc] init]; | |
_delegate = greetingFactory; | |
} | |
//... | |
- (IBAction)onButtonClick:(id)sender { | |
NSString *name = _nameField.text; | |
[_delegate sayHello:name toMyLittleFriend:self]; | |
} | |
//... | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SPEC_BEGIN(HelloTDDViewControllerSpec) | |
describe(@"HelloTDDViewController", ^{ | |
HelloTDDViewController *viewController = | |
[[HelloTDDViewController alloc] init]; | |
MockHelloTDDViewControllerDelegate *delegate = | |
[[MockHelloTDDViewControllerDelegate alloc] init]; | |
//... | |
context(@"when button is pressed", ^{ | |
it(@"should notify delegate.", ^{ | |
viewController.delegate = delegate; | |
[viewController onButtonClick:nil]; | |
[[theValue(delegate.buttonWasClicked) should] beYes]; | |
}); | |
it(@"should pass name to delegate.", ^{ | |
viewController.delegate = delegate; | |
UITextField *textField = [[UITextField alloc] init]; | |
textField.text = @"First Last"; | |
viewController.nameField = textField; | |
[viewController onButtonClick:nil]; | |
[[delegate.lastNameSent should] equal:@"First Last"]; | |
}); | |
}); | |
//... | |
}); | |
SPEC_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@implementation HelloTDDTestController | |
- (void)initializeScenarios { | |
[self addScenario:[KIFTestScenario scenarioToDisplayGreeting]]; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@implementation KIFTestScenario (HelloTDDAdditions) | |
+ (id)scenarioToDisplayGreeting { | |
KIFTestScenario *scenario = | |
[KIFTestScenario scenarioWithDescription: | |
@"Test greeting with name."]; | |
[scenario addStep: | |
[KIFTestStep stepToEnterText: | |
@"Chuck Norris"intoViewWithAccessibilityLabel:@"Name"]]; | |
[scenario addStep: | |
[KIFTestStep stepToTapViewWithAccessibilityLabel: | |
@"Say Hello"]]; | |
[scenario addStep: | |
[KIFTestStep stepToVerifyGreeting: | |
@"Hello, Chuck Norris!"]]; | |
return scenario; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@implementation KIFTestStep (HelloTDDAdditions) | |
+ (id)stepToVerifyGreeting:(NSString *)expectedLabel { | |
NSString *description = | |
[NSString stringWithFormat:@"Verify output is '%@'", | |
expectedLabel]; | |
return [self stepWithDescription:description | |
executionBlock:^(KIFTestStep *step, NSError **error) { | |
UIAccessibilityElement *element = | |
[[UIApplication sharedApplication] | |
accessibilityElementWithLabel:@"Greeting"]; | |
UILabel *label = (UILabel *)[UIAccessibilityElement | |
viewContainingAccessibilityElement:element]; | |
if ([expectedLabel isEqualToString:label.text]) { | |
return KIFTestStepResultSuccess; | |
} | |
KIFTestCondition(NO, error, | |
@"Failed to compare the label text: expected '%@', actual '%@'", | |
expectedLabel, label.text); | |
}]; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@implementation HelloTDDViewController | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
_nameField.accessibilityLabel = @"Name"; | |
_helloLabel.accessibilityLabel = @"Greeting"; | |
GreetingFactory *greetingFactory = [[GreetingFactory alloc] init]; | |
_delegate = greetingFactory; | |
} | |
//... | |
@end |
I'd also recommend checking out specta.
ReplyDeletehttps://github.com/specta/specta