Download - iOS testing
Testing in iOS10.01.2013 by Tomasz Janeczko
About me
Tomasz Janeczko
• iOS developer in Kainos
• Enthusiast of business, electronics, Rails & Heroku
• Organizer of first App Camp in UK and PL
So let’s talk about testing.
Why we test?
So?
• Reliability
• Regression
• Confidence (e.g. refactoring)
Why not to test?
Why not to test?
• Heavy dependence on UI
• Non-testable code
• Bad framework
How to address issues
• Sample - downloading stuff from interwebz
First fault
Writing tests after writing code
Separation of concerns
Separation of concerns
• Let’s separate out the UI code
• Same for services interaction
Demo of tests
Writing testsMeet Kiwi and OCMock
Kiwi
• RSpec-like tests writing
• Matchers
• Cleaner and more self-descriptive code
Kiwi
describe(@"Tested class", ^{
context(@"When created", ^{
it(@"should not fail", ^{ [[theValue(0) should] equal:theValue(0)]; });
});
});
Kiwi
describe(@"Tested class", ^{
context(@"When created", ^{
it(@"should not fail", ^{ id viewController = [ViewController new]; [[viewController should] conformToProtocol:@protocol(UITableViewDelegate)]; });
});
});
Matchers[subject shouldNotBeNil]
• [subject shouldBeNil]
• [[subject should] beIdenticalTo:(id)anObject] - compares id's
• [[subject should] equal:(id)anObject]
• [[subject should] equal:(double)aValue withDelta:(double)aDelta]
• [[subject should] beWithin:(id)aDistance of:(id)aValue]
• [[subject should] beLessThan:(id)aValue]
• etc. etc.
Compare to SenTesting Kit
[[subject should] equal:anObject]
compare with
STAssertEquals(subject, anObject, @”Should be equal”);
OCMock
• Mocking and stubbing library for iOS
• Quite versatile
• Makes use of NSProxy magic
Sample workflows
Classic calculator sample
describe(@"Calculator", ^{ context(@"with the numbers 60 and 5 entered", ^{ RPNCalculator *calculator = [[RPNCalculator alloc] init]; beforeEach(^{ [calculator enter:60]; [calculator enter:5]; });
afterEach(^{
[calculator clear]; }); it(@"returns 65 as the sum", ^{ [[theValue([calculator add]) should] equal:65 withDelta:.01]; });
Test if calls dep methods
1. Create a mock dependency
2. Inject it
3. Call the method
4. Verify
Test dependency
// Create the tested object and mock to exchange part of the functionalityviewController = [ViewController new];mockController = [OCMockObject partialMockForObject:viewController];
// Create the mock and change implementation to return our classid serviceMock = [OCMockObject mockForClass:[InterwebzService class]];[[[mockController stub] andReturn:serviceMock] service]; // Define expectations[[serviceMock expect] downloadTweetsJSONWithSuccessBlock:[OCMArg any]]; // Run the tested method[viewController tweetsButtonTapped:nil]; // Verify - throws exception on failure[mockController verify];
Testing one layer
• Isolate dependencies
• Objective-C is highly dynamic - we can change implementations of private methods or static methods
• We can avoid IoC containers for testing
Accessing private methods
Accessing private methods
@interface ViewController()
- (void)startDownloadingTweets;
@end
...[[mockController expect] startDownloadingTweets];
Static method testing
• Through separation to a method@interface ViewController()
- (NSUserDefaults *)userDefaults;
@end
...
id mockDefaults = [OCMockObject mockForClass:[NSUserDefaults class]];
[[[mockDefaults expect] andReturn:@"Setting"] valueForKey:[OCMArg any]];
[[[mockController stub] andReturn:mockDefaults] userDefaults];
Static method testing
• Through method swizzlingvoid SwizzleClassMethod(Class c, SEL orig, SEL new) {
Method origMethod = class_getClassMethod(c, orig); Method newMethod = class_getClassMethod(c, new);
c = object_getClass((id)c);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); else method_exchangeImplementations(origMethod, newMethod);}
Normal conditions applydespite it’s iOS & Objective--C
Problems of „mobile devs”
• Pushing code with failing tests
• Lot’s of hacking together
• Weak knowledge of VCS tools - merge nightmares
Ending thoughts
• Think first (twice), then code :)
• Tests should come first
• Write the failing test, pass the test, refactor
• Adequate tools can enhance your testing experience
Ending thoughts
• Practice!
Thanks!
Questions