Slide 1

Slide 1 text

Better design through testing: With OCUnit, OCMock Cofounder of Waveface Inc. Jamie Sa @jamex

Slide 2

Slide 2 text

Bike Messenger

Slide 3

Slide 3 text

What is a good design?

Slide 4

Slide 4 text

Agile is good™

Slide 5

Slide 5 text

Law of Demeter The Law of Demeter of functions states that any method of an object should call only methods belonging to: @interface Demeter : NSObject{ @private A *a; } - (void) example: (B *) b; @end @implementation Demeter - (NSUInteger)func { ... } - (void)example:(B *)b { C *c; [self func]; [b invert]; a = [[A alloc] init]; a.active = YES; [c print]; } @end

Slide 6

Slide 6 text

Law of Demeter The Law of Demeter of functions states that any method of an object should call only methods belonging to: @interface Demeter : NSObject{ @private A *a; } - (void) example: (B *) b; @end @implementation Demeter - (NSUInteger)func { ... } - (void)example:(B *)b { C *c; [self func]; [b invert]; a = [[A alloc] init]; a.active = YES; [c print]; } @end itself

Slide 7

Slide 7 text

Law of Demeter The Law of Demeter of functions states that any method of an object should call only methods belonging to: @interface Demeter : NSObject{ @private A *a; } - (void) example: (B *) b; @end @implementation Demeter - (NSUInteger)func { ... } - (void)example:(B *)b { C *c; [self func]; [b invert]; a = [[A alloc] init]; a.active = YES; [c print]; } @end itself any parameters that were passed in to the method

Slide 8

Slide 8 text

Law of Demeter The Law of Demeter of functions states that any method of an object should call only methods belonging to: @interface Demeter : NSObject{ @private A *a; } - (void) example: (B *) b; @end @implementation Demeter - (NSUInteger)func { ... } - (void)example:(B *)b { C *c; [self func]; [b invert]; a = [[A alloc] init]; a.active = YES; [c print]; } @end itself any parameters that were passed in to the method any object it created

Slide 9

Slide 9 text

Law of Demeter The Law of Demeter of functions states that any method of an object should call only methods belonging to: @interface Demeter : NSObject{ @private A *a; } - (void) example: (B *) b; @end @implementation Demeter - (NSUInteger)func { ... } - (void)example:(B *)b { C *c; [self func]; [b invert]; a = [[A alloc] init]; a.active = YES; [c print]; } @end itself any parameters that were passed in to the method any directly held component objects any object it created

Slide 10

Slide 10 text

Testing makes it feels nature

Slide 11

Slide 11 text

An example View Controller Model

Slide 12

Slide 12 text

An example View Controller Model Network

Slide 13

Slide 13 text

An example View Controller Model Network

Slide 14

Slide 14 text

An example View Controller Model Network What should we do with this?

Slide 15

Slide 15 text

Mock is here to save you

Slide 16

Slide 16 text

Mocks are pre-programmed with expectations which form a speci cation of the calls they are expected to receive. - Gerard Meszaros (XUnit Test Patterns)

Slide 17

Slide 17 text

Category @implementation NSUserDefaults (UnitTesting) -(id)valueForKey:(NSString *)key { return @"SetByCategory"; } @end @implementation CategoryTests - (void)testUserDefaultsSwitch { NSString * key = [[NSUserDefaults standardUserDefaults] valueForKey:@"aKey"]; STAssertEquals(key, @"SetByCategory", @"should be nil"); } @end

Slide 18

Slide 18 text

Category is •Not Mock • No expectation • Don’t form a spec •Make a mess •It’s just a Stub. • provides canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test.

Slide 19

Slide 19 text

Method Swizzling #import @interface MYNSUserDefaults : NSUserDefaults @end @implementation MYNSUserDefaults - (id)valueForKeySwizzled:(NSString *)key { return @"SetByMethodSwizzling"; } @end @implementation MethodSwizzlingTests - (void)testUserDefaultsSwitch { Method original = class_getInstanceMethod( [NSUserDefaults class], @selector(valueForKey:)); Method swizzled = class_getInstanceMethod( [MYNSUserDefaults class], @selector(valueForKeySwizzled:)); method_exchangeImplementations(original, swizzled); NSString * key = [[NSUserDefaults standardUserDefaults] valueForKey:@"aKey"]; STAssertEquals(key, @"SetByMethodSwizzling", @"should be nil"); } @end

Slide 20

Slide 20 text

Method Swizzling is •not Mock •less messy (in terms of testing) • only effect current runtime •Could be tedious •Still just a Stub

Slide 21

Slide 21 text

OCMock #import @implementation MockTests - (void) testMockUserDefaults { id mockUserDefaults = [OCMockObject mockForClass:[NSUserDefaults class]]; [[[mockUserDefaults expect] andReturn:@"aKeyFromMock"] valueForKey:@"aKey"]; NSString * key = [mockUserDefaults valueForKey:@"aKey"]; STAssertTrue([key isEqualToString:@"aKeyFromMock"], @"Mocking"); [mockUserDefaults verify]; } @end

Slide 22

Slide 22 text

OCMock is •a real Mock ✓ expect what to expect ✓ form a spec ✓ clean • Many other tricks can be done • Making async calls “sync” • Bypass expensive operations (NSURLConnection)

Slide 23

Slide 23 text

With OCHamcrest #define HC_SHORTHAND #import @implementation MockTests - (void) testMockUserDefaultsWithMatcher { id mockUserDefaults = [OCMockObject mockForClass:[NSUserDefaults class]]; [[[mockUserDefaults expect] andReturn:@"aKeyFromMock"] valueForKey:@"aKey"]; NSString * key = [mockUserDefaults valueForKey:@"aKey"]; assertThat(key, equalTo(@"aKeyFromMock")); [mockUserDefaults verify]; } @end

Slide 24

Slide 24 text

Common good practice •Put les in right target. Not too many, not to less. •Application testing and Unit Testing. •Expect to take 50+% more time with the same feature •Make it into Continuous Integration

Slide 25

Slide 25 text

References •LinkedImageFetcher by Quinn “The Eskimo!” •The Pragmatic Programmer by Andrew Hunt, David Thomas •Intro to Objective-C TDD by Jon Reid