The Cowardly Test-o-Phobe's Guide To Testing


Published on

Slides from a presentation given at iOScon London, May 2014

Published in: Software, Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

The Cowardly Test-o-Phobe's Guide To Testing

  1. 1. The Cowardly 
 Guide To iOS Testing
  2. 2. Why is testing scary? • Because you’re not a computer scientist • Because Java bores you rigid • Because there’s nothing in life more tedious than
 a“thought leader” • Because you get paid to ship apps
  3. 3. Why is testing important? • You’ll write code with fewer bugs in it • You’ll introduce fewer bugs as the project develops • You’ll document the project without writing any docs • You’ll knit yourself a security blanket
  4. 4. In the next 40 mins… • I’ll try to convince you that it’s not all *that* scary • I’ll show you how to get started, even on an existing project • I’ll show you some tools you can start using on Monday
  5. 5. The basics • Does my code do what it’s supposed to do? • Does my code cope with unexpected values? • Does my code break when I change things?
  6. 6. Test first • Write a test to describe what you want your code to do. • Run the test and watch it fail. • Write the code you need to get the test to pass. • Rinse and repeat.
  7. 7. Why not test last? • Because there’s no motivation to test once it runs • Because there’s never time • Because you’re confirming your own prejudices
  8. 8. Test first • Write a failing test - RED • Write the code to make it pass - GREEN • Write the next test • Write the code to make it - and all the previous tests - pass REFACTOR
  9. 9. Challenges of testing iOS • Unit testing is designed around testing code • iOS apps are largely interface driven • How do you test the effect of taps, touches, swipes and pinches?
  10. 10. The tools • You’ll need a test framework • Two basic styles: • JUnit • RSpec • Xcode ships with XCTest, which is a JUnit-style
  11. 11. Which one? • Kiwi - a personal choice, but I dislike the JUnit syntax • Kiwi also includes a nice mocking framework, of which more later • Other alternatives are available • Bottom line: use what you feel most comfortable with
  12. 12. UI testing approaches • “Robot fingers” • Borrowed from the web world • Peers inside the view hierarchy and checks what’s going on • It works, but it’s fiddly to set up, fiddly to use, and SLOW
  13. 13. UI testing approaches • Testing with code • UI interactions are passed down into code via IBAction methods • The IBAction methods are our code, so let’s test that to make sure everything works. • Works on the basis there’s no point in testing other people’s code (especially Apple’s!)
  14. 14. UI testing approaches
  15. 15. The approach • Instantiate your view controller • Recreate your view hierarchy • Manipulate and test your views • Err… • That’s it.
  16. 16. Dependencies • One of the biggest problems in getting started with testing an app is how to handle dependencies • Classes and methods seldom exist in isolation from each other • What happens if you are reliant on external data sources such as network APIs? • How do you test your code without nailing up all the supporting objects?
  17. 17. Mocking and stubbing • How to do it Mocking
  19. 19. Mocking • Mocking is the process of creating“stunt doubles” • The mock object stands in for the real thing • Also known as“duck typing” • If it looks like a duck, and swims like a duck, and quacks like a duck… it probably is a duck.
  20. 20. Stubbing • Stubbing takes an existing object, and returns a value that you provide • You can stub mocks that you’ve created • You can also stub real objects to return canned values
  21. 21. Mocking • Creating mock objects: id myMockedSubclass = [MyClass mock]; • Stubbing methods without return values: ! [myMockedSubclass stub:@selector(stubbedMethod)]; ! [myMockedSubclass stubbedMethod]; • Stubbing methods with return values: ! [myMockedSubclass stub:@selector(myMethod) 
 andReturn:@"the return value”]; ! NSString *returnValue = [myMockedSubclass myMethod];
  22. 22. Mocking your objects id theEnterprise = [Starship mock]; [theEnterprise stub:@selector(warpFactor) andReturn:@9]; ... [communicator report:[theEnterprise warpFactor]];
  23. 23. Mocking“real”objects id mockDefaults = [NSUserDefaults mock]; [[mockDefaults stubAndReturn:@10]
 valueForKey:@"userId"]; [[[mockDefaults valueForKey:@"userId"] should] equal:@10]; ... NSNumber *userId = [mockDefaults objectForKey:@"userId"];
  24. 24. Network testing
  25. 25. Testing networks • What do you do if your tests imply a dependency on a network data source? • How do you handle variations in responses? • How do you handle latency? • How do you deal with throttling and rate limits?
  26. 26. Approaches • Set up a“stunt double”API using something like Node or Sinatra • Stub network calls and return“canned”values from within your tests
  27. 27. OHHTTPStubs • Returns“canned”values in response to network calls, e.g. from files that you embed in the project • Can simulate different types of network speed • Can simulate slow API responses so that you can test how to handle progress indicators or timeouts
  28. 28. Capturing the data • Grab the data using wget and save it into a file wget “” -O response.json • Add the response file to your project bundle • Serve the response file with OHHTTPStubs
  29. 29. Mocking a response [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { Examine the URL components: - baseURL - path
 - relativePath - parameterString etc etc etc Return TRUE when matched } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { Build and return an OHHTTPStubsResponse object: - load a file from the local filesystem - send back a specific HTTP status code - send back custom headers - send back errors - adjust response and request lead times
  30. 30. Capturing the data [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return [request.URL.path isEqualToString:@"/v1/connections"]; } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { return [[OHHTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(@"v1_con.json", nil) statusCode:200 headers:@{@"Content-Type":@"text/json"}] requestTime:4.0f responseTime:1.0f]; ! }];
  31. 31. and finally… • Test your user interfaces in code! • Mock and stub your classes to handle internal dependencies! • Fake network connections! • Testing doesn’t have to be scary! , ,
  32. 32. I am Twitter, GitHub et al
 @timd ! We are
 and are hiring!