Slide 1

Slide 1 text

Testing in iOS 10.01.2013 by Tomasz Janeczko

Slide 2

Slide 2 text

About me Tomasz Janeczko • iOS developer in Kainos • Enthusiast of business, electronics, Rails & Heroku • Organizer of first App Camp in UK and PL

Slide 3

Slide 3 text

So let’s talk about testing.

Slide 4

Slide 4 text

Why we test?

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

So?

Slide 7

Slide 7 text

• Reliability • Regression • Confidence (e.g. refactoring)

Slide 8

Slide 8 text

Why not to test?

Slide 9

Slide 9 text

Why not to test? • Heavy dependence on UI • Non-testable code • Bad framework

Slide 10

Slide 10 text

How to address issues • Sample - downloading stuff from interwebz

Slide 11

Slide 11 text

First fault Writing tests after writing code

Slide 12

Slide 12 text

Separation of concerns

Slide 13

Slide 13 text

Separation of concerns • Let’s separate out the UI code • Same for services interaction

Slide 14

Slide 14 text

Demo of tests

Slide 15

Slide 15 text

Writing tests Meet Kiwi and OCMock

Slide 16

Slide 16 text

Kiwi • RSpec-like tests writing • Matchers • Cleaner and more self-descriptive code

Slide 17

Slide 17 text

Kiwi describe(@"Tested class", ^{ context(@"When created", ^{ it(@"should not fail", ^{ [[theValue(0) should] equal:theValue(0)]; }); }); });

Slide 18

Slide 18 text

Kiwi describe(@"Tested class", ^{ context(@"When created", ^{ it(@"should not fail", ^{ id viewController = [ViewController new]; [[viewController should] conformToProtocol:@protocol(UITableViewDelegate)]; }); }); });

Slide 19

Slide 19 text

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.

Slide 20

Slide 20 text

Compare to SenTesting Kit [[subject  should]  equal:anObject] compare  with STAssertEquals(subject,  anObject,  @”Should  be  equal”);

Slide 21

Slide 21 text

OCMock • Mocking and stubbing library for iOS • Quite versatile • Makes use of NSProxy magic

Slide 22

Slide 22 text

Sample workflows

Slide 23

Slide 23 text

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];                });

Slide 24

Slide 24 text

Test if calls dep methods 1. Create a mock dependency 2. Inject it 3. Call the method 4. Verify

Slide 25

Slide 25 text

Test dependency // Create the tested object and mock to exchange part of the functionality viewController = [ViewController new]; mockController = [OCMockObject partialMockForObject:viewController]; // Create the mock and change implementation to return our class id 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];

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

Accessing private methods

Slide 28

Slide 28 text

Accessing private methods @interface ViewController() - (void)startDownloadingTweets; @end ... [[mockController expect] startDownloadingTweets];

Slide 29

Slide 29 text

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];

Slide 30

Slide 30 text

Static method testing • Through method swizzling void  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); }

Slide 31

Slide 31 text

Normal conditions apply despite it’s iOS & Objective--C

Slide 32

Slide 32 text

Problems of „mobile devs” • Pushing code with failing tests • Lot’s of hacking together • Weak knowledge of VCS tools - merge nightmares

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Ending thoughts • Practice!

Slide 35

Slide 35 text

Thanks!

Slide 36

Slide 36 text

Questions