Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Better Design Through Testing

jsa :~
January 10, 2013

Better Design Through Testing

jsa :~

January 10, 2013
Tweet

More Decks by jsa :~

Other Decks in Technology

Transcript

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. Mocks are pre-programmed with expectations which form a speci cation

    of the calls they are expected to receive. - Gerard Meszaros (XUnit Test Patterns)
  7. 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
  8. 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.
  9. Method Swizzling #import <objc/runtime.h> @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
  10. Method Swizzling is •not Mock •less messy (in terms of

    testing) • only effect current runtime •Could be tedious •Still just a Stub
  11. OCMock #import <OCMock/OCMock.h> @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
  12. 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)
  13. With OCHamcrest #define HC_SHORTHAND #import <OCHamcrest/OCHamcrest.h> @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
  14. 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
  15. References •LinkedImageFetcher by Quinn “The Eskimo!” •The Pragmatic Programmer by

    Andrew Hunt, David Thomas •Intro to Objective-C TDD by Jon Reid