Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Taking a Test Drive

699 views

Published on

Recipes for test-driven development of iOS applications.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Taking a Test Drive

  1. 1. TAKING A TEST DRIVE Graham Lee, Fuzzy Aliens Limited fuzzyaliens.com
  2. 2. TAKING A TEST DRIVE Graham Lee, Fuzzy Aliens Limited
  3. 3. Pants Pants✘ ✔ ✘ Trousers
  4. 4. PREVIOUSLY ON VTM…STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”);
  5. 5. PREVIOUSLY ON VTM…STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”);
  6. 6. PREVIOUSLY ON VTM…STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”); @ddribin
  7. 7. Episode 1 SummarySoftware should satisfy the users’ needsTesting demonstrates that this is true Unit testing can be done by the developer Cheap Well-situated Test-Driven Development is a design practice
  8. 8. This week, on VTM…- (void)testBlah { STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”);}
  9. 9. This week, on VTM…OC_TEST_CASE(“General/IsOn”, “Is this thing on?”) { REQUIRE(thisThing.state == NSOnState);} https://github.com/philsquared/Catch @phil_nash
  10. 10. Rule 0
  11. 11. Rule 0Model-View-Controller
  12. 12. Recipe: IBOutlets- (void)viewDidLoad { NSAssert(myOutlet != nil); //...}
  13. 13. Recipe: IBOutlets- (void)setUp { ctrl = [[MyViewController alloc] initWithNibName: nil bundle: nil]; label = [[UILabel alloc] init]; label.text = @"Label"; ctrl.label = label;}OC_TEST_CASE(“MyViewController/SetText”, “Text should be updated”) { [ctrl setText]; REQUIRE([label.text isEqualToString: @"Set"]);}- (void)tearDown { [ctrl release]; [label release];}
  14. 14. Recipe: IBActions- (IBAction)doSomething;- (IBAction)doSomething: (id)sender;- (IBAction)doSomething: (id)sender forEvent: (UIEvent *)event;
  15. 15. Recipe: Core Data
  16. 16. Recipe: Core DataRule 1 of testing Core Data:
  17. 17. Recipe: Core DataRule 1 of testing Core Data:don’t talk about Core Data
  18. 18. Recipe: Core Data
  19. 19. Recipe: Core Data
  20. 20. Recipe: Core Data- (IBAction)addStaff: (id)sender { NSManagedObject *employee = [[NSManagedObject alloc] initWithEntity: employeeEntity insertIntoManagedObjectContext: moc]; employee.lastName = @"Smith";}- (IBAction)addStaff: (id)sender { id <Employee> employee = [self.employeeBuilder newEmployee]; employee.lastName = @"Smith";}
  21. 21. Aside: Builder
  22. 22. Recipe: Core Data
  23. 23. Recipe: Core DataRule 2 of testing Core Data:
  24. 24. Recipe: Core DataRule 2 of testing Core Data: don’t use a database
  25. 25. A test is not a unit test if:It talks to the database Michael Feathers, idealized unit testIt communicates across the networkIt touches the file systemIt cant run at the same time as any of your other unit testsYou have to do special things to your environment
  26. 26. Recipe: Core Data@interface TheTest : NSObject <OcFixture> {@private NSPersistentStoreCoordinator *coord; NSManagedObjectContext *ctx; NSManagedObjectModel *model; NSPersistentStore *store;}@end
  27. 27. Recipe: Core Data@implementation TheTest- (void)setUp { model = [[NSManagedObjectModel mergedModelFromBundles: nil] retain]; coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: model]; store = [coord addPersistentStoreWithType: NSInMemoryStoreType configuration: nil URL: nil options: nil error: NULL]; ctx = [[NSManagedObjectContext alloc] init]; [ctx setPersistentStoreCoordinator: coord];}- (void)tearDown { [ctx release]; ctx = nil; [coord removePersistentStore: store error: NULL]; store = nil; [coord release]; coord = nil; [model release]; model = nil;}OC_TEST_CASE("CoreData/Store", "Ensure the persistent store was set up.") { REQUIRE(store != nil);}@end
  28. 28. Recipe: Core Data@implementation TheTest- (void)setUp { model = [[NSManagedObjectModel mergedModelFromBundles: nil] retain]; coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: model]; store = [coord addPersistentStoreWithType: NSInMemoryStoreType configuration: nil URL: nil options: nil error: NULL]; ctx = [[NSManagedObjectContext alloc] init]; [ctx setPersistentStoreCoordinator: coord];}- (void)tearDown { [ctx release]; ctx = nil; [coord removePersistentStore: store error: NULL]; store = nil; [coord release]; coord = nil; [model release]; model = nil;}OC_TEST_CASE("CoreData/Store", "Ensure the persistent store was set up.") { REQUIRE(store != nil);}@end
  29. 29. Recipe: Core Data@implementation TheTest- (void)setUp { model = [[NSManagedObjectModel mergedModelFromBundles: nil] retain]; coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: model]; store = [coord addPersistentStoreWithType: NSInMemoryStoreType configuration: nil URL: nil options: nil error: NULL]; ctx = [[NSManagedObjectContext alloc] init]; [ctx setPersistentStoreCoordinator: coord];}- (void)tearDown { [ctx release]; ctx = nil; [coord removePersistentStore: store error: NULL]; store = nil; [coord release]; coord = nil; [model release]; model = nil;}OC_TEST_CASE("CoreData/Store", "Ensure the persistent store was set up.") { REQUIRE(store != nil);}@end
  30. 30. Recipe: Core Data@implementation TheTest- (void)setUp { model = [[NSManagedObjectModel mergedModelFromBundles: nil] retain]; coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: model]; store = [coord addPersistentStoreWithType: NSInMemoryStoreType configuration: nil URL: nil options: nil error: NULL]; ctx = [[NSManagedObjectContext alloc] init]; [ctx setPersistentStoreCoordinator: coord];}- (void)tearDown { [ctx release]; ctx = nil; [coord removePersistentStore: store error: NULL]; store = nil; [coord release]; coord = nil; [model release]; model = nil;}OC_TEST_CASE("CoreData/Store", "Ensure the persistent store was set up.") { REQUIRE(store != nil);}@end
  31. 31. Recipe: Core Data@implementation TheTest- (void)setUp { model = [[NSManagedObjectModel mergedModelFromBundles: nil] retain]; coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: model]; store = [coord addPersistentStoreWithType: NSInMemoryStoreType configuration: nil URL: nil options: nil error: NULL]; ctx = [[NSManagedObjectContext alloc] init]; [ctx setPersistentStoreCoordinator: coord];}- (void)tearDown { [ctx release]; ctx = nil; [coord removePersistentStore: store error: NULL]; store = nil; [coord release]; coord = nil; [model release]; model = nil;}OC_TEST_CASE("CoreData/Store", "Ensure the persistent store was set up.") { REQUIRE(store != nil);}@end
  32. 32. Recipe: NSNotificationCenterActually talking about Singletons in generalRule #1 of Singletons is don’t use SingletonsCocoa Touch doesn’t give us much choice
  33. 33. Recipe: NSNotificationCenterActually talking about Singletons in generalRule #1 of Singletons is don’t use SingletonsCocoa Touch doesn’t give us much choice Remember, a singleton is a global in sheep’s clothing.
  34. 34. Recipe: NSNotificationCenter- (void)watchForThingsHappening { [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(workDidFinish:) name: WorkDidFinishNotification object: worker];}OC_TEST_CASE("ViewController/WorkerFinished", "Ensure weknow when the works done") { [controller watchForThingsHappening]; REQUIRE(/*???*/);}
  35. 35. Recipe: NSNotificationCenter- (void)watchForThingsHappening: (NSNotificationCenter *)center { [center addObserver: self selector: @selector(workDidFinish:) name: WorkDidFinishNotification object: worker];}OC_TEST_CASE("ViewController/WorkerFinished", "Ensure we know whenthe works done") { [controller watchForThingsHappening: mockNotificationCenter]; REQUIRE([mockNotificationCenter hasObserver: controller forNotificationNamed: WorkDidFinishNotification]);}
  36. 36. Conclusions
  37. 37. ConclusionsTDD implies a particular approach to writing APIs
  38. 38. ConclusionsTDD implies a particular approach to writing APIsCertain patterns support this approach way
  39. 39. ConclusionsTDD implies a particular approach to writing APIsCertain patterns support this approach wayThe patterns you’ve seen today work well with TDD and with theCocoa Touch SDK
  40. 40. THANKS!http://blog.securemacprogramming.com/ @iamleeg

×