ºÝºÝߣ

ºÝºÝߣShare a Scribd company logo
Testing in iOS
10.01.2013 by Tomasz Janeczko
About me
Tomasz Janeczko
? iOS developer in Kainos
? Enthusiast of business, electronics, Rails
  & Heroku
? Organizer of ?rst App Camp in UK and
  PL
So let¡¯s talk about testing.
Why we test?
iOS testing
So?
? Reliability

? Regression

? Con?dence (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 tests
Meet 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 work?ows
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 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];
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 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);
}
Normal conditions apply
   despite 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 ?rst (twice), then code :)
? Tests should come ?rst
? Write the failing test, pass the test,
  refactor
? Adequate tools can enhance your
  testing experience
Ending thoughts



? Practice!
Thanks!
Questions

More Related Content

iOS testing

  • 1. Testing in iOS 10.01.2013 by Tomasz Janeczko
  • 2. About me Tomasz Janeczko ? iOS developer in Kainos ? Enthusiast of business, electronics, Rails & Heroku ? Organizer of ?rst App Camp in UK and PL
  • 3. So let¡¯s talk about testing.
  • 6. So?
  • 7. ? Reliability ? Regression ? Con?dence (e.g. refactoring)
  • 8. Why not to test?
  • 9. Why not to test? ? Heavy dependence on UI ? Non-testable code ? Bad framework
  • 10. How to address issues ? Sample - downloading stuff from interwebz
  • 11. First fault Writing tests after writing code
  • 13. Separation of concerns ? Let¡¯s separate out the UI code ? Same for services interaction
  • 16. Kiwi ? RSpec-like tests writing ? Matchers ? Cleaner and more self-descriptive code
  • 17. Kiwi describe(@"Tested class", ^{ context(@"When created", ^{ it(@"should not fail", ^{ [[theValue(0) should] equal:theValue(0)]; }); }); });
  • 18. Kiwi describe(@"Tested class", ^{ context(@"When created", ^{ it(@"should not fail", ^{ id viewController = [ViewController new]; [[viewController should] conformToProtocol:@protocol(UITableViewDelegate)]; }); }); });
  • 19. 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.
  • 20. Compare to SenTesting Kit [[subject ?should] ?equal:anObject] compare ?with STAssertEquals(subject, ?anObject, ?@¡±Should ?be ?equal¡±);
  • 21. OCMock ? Mocking and stubbing library for iOS ? Quite versatile ? Makes use of NSProxy magic
  • 23. 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]; ? ? ? ? ? ? ? ?});
  • 24. Test if calls dep methods 1. Create a mock dependency 2. Inject it 3. Call the method 4. Verify
  • 25. 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];
  • 26. 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
  • 28. Accessing private methods @interface ViewController() - (void)startDownloadingTweets; @end ... [[mockController expect] startDownloadingTweets];
  • 29. 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];
  • 30. 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); }
  • 31. Normal conditions apply despite it¡¯s iOS & Objective--C
  • 32. Problems of ?mobile devs¡± ? Pushing code with failing tests ? Lot¡¯s of hacking together ? Weak knowledge of VCS tools - merge nightmares
  • 33. Ending thoughts ? Think ?rst (twice), then code :) ? Tests should come ?rst ? Write the failing test, pass the test, refactor ? Adequate tools can enhance your testing experience