SlideShare a Scribd company logo
TAKING A TEST DRIVE
     Graham Lee, Fuzzy Aliens Limited




                                    fuzzyaliens.com
TAKING A TEST DRIVE
     Graham Lee, Fuzzy Aliens Limited
Taking a Test Drive
Pants              Pants




✘             ✔                ✘
            Trousers
PREVIOUSLY ON VTM…
STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”);
PREVIOUSLY ON VTM…
STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”);
PREVIOUSLY ON VTM…
STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”);




                                                            @ddribin
Episode 1 Summary
Software should satisfy the users’ needs

Testing demonstrates that this is true

  Unit testing can be done by the developer

    Cheap

    Well-situated

  Test-Driven Development is a design practice
This week, on VTM…

- (void)testBlah {
  STAssertTrue(thisThing.state == NSOnState,
          @”Is this thing on?”);
}
This week, on VTM…

OC_TEST_CASE(“General/IsOn”, “Is this thing on?”) {
  REQUIRE(thisThing.state == NSOnState);
}




            https://github.com/philsquared/Catch

                    @phil_nash
Rule 0
Rule 0




Model-View-Controller
Recipe: IBOutlets

- (void)viewDidLoad {
   NSAssert(myOutlet != nil);
   //...
}
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];
}
Recipe: IBActions

- (IBAction)doSomething;


- (IBAction)doSomething: (id)sender;


- (IBAction)doSomething: (id)sender forEvent: (UIEvent *)event;
Recipe: Core Data
Recipe: Core Data


Rule 1 of testing Core Data:
Recipe: Core Data


Rule 1 of testing Core Data:


don’t talk about Core Data
Recipe: Core Data
Recipe: Core Data
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";
}
Aside: Builder
Recipe: Core Data
Recipe: Core Data


Rule 2 of testing Core Data:
Recipe: Core Data


Rule 2 of testing Core Data:


   don’t use a database
A test is not a unit test if:

It talks to the database                Michael Feathers, idealized unit
                                        test
It communicates across the network

It touches the file system

It can't run at the same time as any of your other unit tests

You have to do special things to your environment
Recipe: Core Data
@interface TheTest : NSObject <OcFixture> {
@private
  NSPersistentStoreCoordinator *coord;
  NSManagedObjectContext *ctx;
  NSManagedObjectModel *model;
  NSPersistentStore *store;
}
@end
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
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
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
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
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
Recipe: NSNotificationCenter


Actually talking about Singletons in general

Rule #1 of Singletons is don’t use Singletons

Cocoa Touch doesn’t give us much choice
Recipe: NSNotificationCenter


Actually talking about Singletons in general

Rule #1 of Singletons is don’t use Singletons

Cocoa Touch doesn’t give us much choice

                   Remember, a singleton is a global in sheep’s clothing.
Recipe: NSNotificationCenter
- (void)watchForThingsHappening {
   [[NSNotificationCenter defaultCenter]
     addObserver: self
       selector: @selector(workDidFinish:)
          name: WorkDidFinishNotification
        object: worker];
}




OC_TEST_CASE("ViewController/WorkerFinished", "Ensure we
know when the work's done") {
  [controller watchForThingsHappening];
  REQUIRE(/*???*/);
}
Recipe: NSNotificationCenter
- (void)watchForThingsHappening: (NSNotificationCenter *)center {
   [center addObserver: self
          selector: @selector(workDidFinish:)
             name: WorkDidFinishNotification
            object: worker];
}



OC_TEST_CASE("ViewController/WorkerFinished", "Ensure we know when
the work's done") {
  [controller watchForThingsHappening: mockNotificationCenter];
  REQUIRE([mockNotificationCenter hasObserver: controller
                 forNotificationNamed:
                     WorkDidFinishNotification]);
}
Conclusions
Conclusions


TDD implies a particular approach to writing APIs
Conclusions


TDD implies a particular approach to writing APIs

Certain patterns support this approach way
Conclusions


TDD implies a particular approach to writing APIs

Certain patterns support this approach way

The patterns you’ve seen today work well with TDD and with the
Cocoa Touch SDK
THANKS!

http://blog.securemacprogramming.com/


       @iamleeg

More Related Content

PDF
Adventures In JavaScript Testing
PPTX
CocoaHeads Moscow. Азиз Латыпов, VIPole. «Запросы в CoreData с агрегатными фу...
PPTX
ES6, 잘 쓰고 계시죠?
PDF
Good Tests Bad Tests
PDF
Django
PDF
Unit Testing: Special Cases
PPTX
Code refactoring of existing AutoTest to PageObject pattern
Adventures In JavaScript Testing
CocoaHeads Moscow. Азиз Латыпов, VIPole. «Запросы в CoreData с агрегатными фу...
ES6, 잘 쓰고 계시죠?
Good Tests Bad Tests
Django
Unit Testing: Special Cases
Code refactoring of existing AutoTest to PageObject pattern

What's hot (20)

KEY
Testing My Patience
PDF
To Err Is Human
PDF
Testing your javascript code with jasmine
PPTX
The uniform interface is 42
PDF
COScheduler In Depth
PDF
Js 单元测试框架介绍
PDF
Spring 4 - A&BP CC
PDF
Clean Test Code
PDF
Tomcat连接池配置方法V2.1
PDF
От экспериментов с инфраструктурой до внедрения в продакшен
PDF
Working with AFNetworking
PDF
Building stable testing by isolating network layer
PDF
Javascript call ObjC
PDF
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
DOCX
Fia fabila
PDF
201913001 khairunnisa progres_harian
TXT
Maze
PDF
Тестирование и Django
PDF
Tugas 2
PDF
Everybody Loves AFNetworking ... and So Can you!
Testing My Patience
To Err Is Human
Testing your javascript code with jasmine
The uniform interface is 42
COScheduler In Depth
Js 单元测试框架介绍
Spring 4 - A&BP CC
Clean Test Code
Tomcat连接池配置方法V2.1
От экспериментов с инфраструктурой до внедрения в продакшен
Working with AFNetworking
Building stable testing by isolating network layer
Javascript call ObjC
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Fia fabila
201913001 khairunnisa progres_harian
Maze
Тестирование и Django
Tugas 2
Everybody Loves AFNetworking ... and So Can you!
Ad

Viewers also liked (15)

PDF
Introduction To Come Recommended
PDF
Come Recommended's Best Advice for Job Seekers in 2010
KEY
Unit testing for Cocoa developers
PDF
Intel Briefing Notes
KEY
Dial M For Mitigation
KEY
Crypto storage
PDF
How to Rev Up Your Job Search, Even if You're Happily Employed
ZIP
Presentations and Podcasts - OxMug July 2009
ZIP
Designing a Secure Cocoa App
PDF
Sign your code
PDF
Beyond build and analyze
KEY
Taking a Test Drive: iOS Dev UK guide to TDD
PDF
Lies, Damned Lies & Internships: Introduction
KEY
Security and Encryption on iOS
PPTX
Why You Need to Hire an HR Tech PR Firm
Introduction To Come Recommended
Come Recommended's Best Advice for Job Seekers in 2010
Unit testing for Cocoa developers
Intel Briefing Notes
Dial M For Mitigation
Crypto storage
How to Rev Up Your Job Search, Even if You're Happily Employed
Presentations and Podcasts - OxMug July 2009
Designing a Secure Cocoa App
Sign your code
Beyond build and analyze
Taking a Test Drive: iOS Dev UK guide to TDD
Lies, Damned Lies & Internships: Introduction
Security and Encryption on iOS
Why You Need to Hire an HR Tech PR Firm
Ad

Similar to Taking a Test Drive (20)

PDF
Core data intermediate Workshop at NSSpain 2013
PDF
Intro to Core Data
PPT
Core data orlando i os dev group
PDF
Core data WIPJam workshop @ MWC'14
PDF
Core Data with Swift 3.0
PDF
CoreData
KEY
iOSDevCamp 2011 Core Data
PDF
Infinum iOS Talks S01E02 - Things every iOS developer should know about Core ...
PDF
CoreData Best Practices (2021)
PDF
Core Data with multiple managed object contexts
PDF
CoreData - there is an ORM you can like!
PDF
Adventures in Multithreaded Core Data
PDF
Core data basic Workshop slides NSSpain 2013
KEY
Data perisistance i_os
KEY
Data perisistence in iOS
PDF
High Performance Core Data
PDF
Simpler Core Data with RubyMotion
PDF
Qardio experience with Core Data
PDF
Advanced Core Data - The Things You Thought You Could Ignore
ODP
MobileCity:Core Data
Core data intermediate Workshop at NSSpain 2013
Intro to Core Data
Core data orlando i os dev group
Core data WIPJam workshop @ MWC'14
Core Data with Swift 3.0
CoreData
iOSDevCamp 2011 Core Data
Infinum iOS Talks S01E02 - Things every iOS developer should know about Core ...
CoreData Best Practices (2021)
Core Data with multiple managed object contexts
CoreData - there is an ORM you can like!
Adventures in Multithreaded Core Data
Core data basic Workshop slides NSSpain 2013
Data perisistance i_os
Data perisistence in iOS
High Performance Core Data
Simpler Core Data with RubyMotion
Qardio experience with Core Data
Advanced Core Data - The Things You Thought You Could Ignore
MobileCity:Core Data

Recently uploaded (20)

PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
Cloud computing and distributed systems.
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Modernizing your data center with Dell and AMD
PDF
Encapsulation theory and applications.pdf
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPTX
Understanding_Digital_Forensics_Presentation.pptx
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
NewMind AI Monthly Chronicles - July 2025
Building Integrated photovoltaic BIPV_UPV.pdf
Review of recent advances in non-invasive hemoglobin estimation
Chapter 3 Spatial Domain Image Processing.pdf
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
“AI and Expert System Decision Support & Business Intelligence Systems”
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Cloud computing and distributed systems.
Dropbox Q2 2025 Financial Results & Investor Presentation
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Modernizing your data center with Dell and AMD
Encapsulation theory and applications.pdf
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Reach Out and Touch Someone: Haptics and Empathic Computing
Understanding_Digital_Forensics_Presentation.pptx
The AUB Centre for AI in Media Proposal.docx
NewMind AI Monthly Chronicles - July 2025

Taking a Test Drive

  • 1. TAKING A TEST DRIVE Graham Lee, Fuzzy Aliens Limited fuzzyaliens.com
  • 2. TAKING A TEST DRIVE Graham Lee, Fuzzy Aliens Limited
  • 4. Pants Pants ✘ ✔ ✘ Trousers
  • 5. PREVIOUSLY ON VTM… STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”);
  • 6. PREVIOUSLY ON VTM… STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”);
  • 7. PREVIOUSLY ON VTM… STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”); @ddribin
  • 8. Episode 1 Summary Software should satisfy the users’ needs Testing demonstrates that this is true Unit testing can be done by the developer Cheap Well-situated Test-Driven Development is a design practice
  • 9. This week, on VTM… - (void)testBlah { STAssertTrue(thisThing.state == NSOnState, @”Is this thing on?”); }
  • 10. This week, on VTM… OC_TEST_CASE(“General/IsOn”, “Is this thing on?”) { REQUIRE(thisThing.state == NSOnState); } https://github.com/philsquared/Catch @phil_nash
  • 13. Recipe: IBOutlets - (void)viewDidLoad { NSAssert(myOutlet != nil); //... }
  • 14. 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]; }
  • 15. Recipe: IBActions - (IBAction)doSomething; - (IBAction)doSomething: (id)sender; - (IBAction)doSomething: (id)sender forEvent: (UIEvent *)event;
  • 17. Recipe: Core Data Rule 1 of testing Core Data:
  • 18. Recipe: Core Data Rule 1 of testing Core Data: don’t talk about Core Data
  • 21. 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"; }
  • 24. Recipe: Core Data Rule 2 of testing Core Data:
  • 25. Recipe: Core Data Rule 2 of testing Core Data: don’t use a database
  • 26. A test is not a unit test if: It talks to the database Michael Feathers, idealized unit test It communicates across the network It touches the file system It can't run at the same time as any of your other unit tests You have to do special things to your environment
  • 27. Recipe: Core Data @interface TheTest : NSObject <OcFixture> { @private NSPersistentStoreCoordinator *coord; NSManagedObjectContext *ctx; NSManagedObjectModel *model; NSPersistentStore *store; } @end
  • 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. 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. 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. 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. 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
  • 33. Recipe: NSNotificationCenter Actually talking about Singletons in general Rule #1 of Singletons is don’t use Singletons Cocoa Touch doesn’t give us much choice
  • 34. Recipe: NSNotificationCenter Actually talking about Singletons in general Rule #1 of Singletons is don’t use Singletons Cocoa Touch doesn’t give us much choice Remember, a singleton is a global in sheep’s clothing.
  • 35. Recipe: NSNotificationCenter - (void)watchForThingsHappening { [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(workDidFinish:) name: WorkDidFinishNotification object: worker]; } OC_TEST_CASE("ViewController/WorkerFinished", "Ensure we know when the work's done") { [controller watchForThingsHappening]; REQUIRE(/*???*/); }
  • 36. Recipe: NSNotificationCenter - (void)watchForThingsHappening: (NSNotificationCenter *)center { [center addObserver: self selector: @selector(workDidFinish:) name: WorkDidFinishNotification object: worker]; } OC_TEST_CASE("ViewController/WorkerFinished", "Ensure we know when the work's done") { [controller watchForThingsHappening: mockNotificationCenter]; REQUIRE([mockNotificationCenter hasObserver: controller forNotificationNamed: WorkDidFinishNotification]); }
  • 38. Conclusions TDD implies a particular approach to writing APIs
  • 39. Conclusions TDD implies a particular approach to writing APIs Certain patterns support this approach way
  • 40. Conclusions TDD implies a particular approach to writing APIs Certain patterns support this approach way The patterns you’ve seen today work well with TDD and with the Cocoa Touch SDK

Editor's Notes

  • #2: \n
  • #3: \n
  • #4: \n
  • #5: \n
  • #6: \n
  • #7: \n
  • #8: \n
  • #9: \n
  • #10: \n
  • #11: \n
  • #12: \n
  • #13: \n
  • #14: \n
  • #15: \n
  • #16: The idea is to provide a set of recipes that you can use in testing iPhone apps. Note that I won&amp;#x2019;t talk about the automated UI testing Instrument - that&amp;#x2019;s not TDD or unit testing.\n
  • #17: The idea is to provide a set of recipes that you can use in testing iPhone apps. Note that I won&amp;#x2019;t talk about the automated UI testing Instrument - that&amp;#x2019;s not TDD or unit testing.\n
  • #18: The idea is to provide a set of recipes that you can use in testing iPhone apps. Note that I won&amp;#x2019;t talk about the automated UI testing Instrument - that&amp;#x2019;s not TDD or unit testing.\n
  • #19: The idea is to provide a set of recipes that you can use in testing iPhone apps. Note that I won&amp;#x2019;t talk about the automated UI testing Instrument - that&amp;#x2019;s not TDD or unit testing.\n
  • #20: The idea is to provide a set of recipes that you can use in testing iPhone apps. Note that I won&amp;#x2019;t talk about the automated UI testing Instrument - that&amp;#x2019;s not TDD or unit testing.\n
  • #21: I&amp;#x2019;ve previously said that NeXT should never have allowed ObjC code that didn&amp;#x2019;t follow MVC to compile. I still stand by that.\n
  • #22: The most common thing I get wrong with outlets is to not set them in the NIB. My strategy for dealing with this is to assert that the outlet is not nil so the app will die if I forget.\n\n
  • #23: There&amp;#x2019;s no problem with programmatically creating views in test code, you don&amp;#x2019;t even need to provide a frame if not necessary. That avoids relying on the XIB in test cases, which is environmental data. For complicated views, it may be easier to fake them.\n
  • #24: The arrow shows increasing &amp;#x201C;tell, don&amp;#x2019;t ask&amp;#x201D;-ness. If you use the first action type, the action method has to find out what it needs to know. That means introspection, which is harder to test than explicit demand (&amp;#x201C;no, use _this_ button&amp;#x201D;).\n
  • #25: \n
  • #26: \n
  • #27: \n
  • #28: \n
  • #29: \n
  • #30: The Builder pattern allows you to decouple the construction of objects from their use. This is automatic to some extent in ObjC because of the way Class objects work, but in this case it&amp;#x2019;s good to have an object that returns a class of a type you define.\n
  • #31: \n
  • #32: \n
  • #33: It&amp;#x2019;s also important to look at what a unit test is not. How to avoid I/O -&gt; fake/mock objects. In-memory CoreData. Textcast has 212 unit tests that run in 0.6 seconds.\n\n&amp;#x201C;Tests that do these things aren&apos;t bad. Often they are worth writing, and they can be written in a unit test harness. However, it is important to be able to separate them from true unit tests so that we can keep a set of tests that we can run fast whenever we make our changes&amp;#x201D;\n
  • #34: \n
  • #35: Note that this does violate the &amp;#x201C;don&amp;#x2019;t depend on data&amp;#x201D; principle, but Core Data is useless without a MOM so you need to do this. The real deal is the in-memory store. Note that an error is ignored in -tearDown, but if you have that error then you should write a test anyway. As I did to ensure that the store is correctly set up.\n
  • #36: Note that this does violate the &amp;#x201C;don&amp;#x2019;t depend on data&amp;#x201D; principle, but Core Data is useless without a MOM so you need to do this. The real deal is the in-memory store. Note that an error is ignored in -tearDown, but if you have that error then you should write a test anyway. As I did to ensure that the store is correctly set up.\n
  • #37: Note that this does violate the &amp;#x201C;don&amp;#x2019;t depend on data&amp;#x201D; principle, but Core Data is useless without a MOM so you need to do this. The real deal is the in-memory store. Note that an error is ignored in -tearDown, but if you have that error then you should write a test anyway. As I did to ensure that the store is correctly set up.\n
  • #38: Note that this does violate the &amp;#x201C;don&amp;#x2019;t depend on data&amp;#x201D; principle, but Core Data is useless without a MOM so you need to do this. The real deal is the in-memory store. Note that an error is ignored in -tearDown, but if you have that error then you should write a test anyway. As I did to ensure that the store is correctly set up.\n
  • #39: NSNotificationCenter is a good example of a singleton that app code often makes use of. Singletons encourage &amp;#x201C;ask, don&amp;#x2019;t tell&amp;#x201D; because it&amp;#x2019;s easy to grab the shared instance from anywhere. The problem is that this means relying on the code&amp;#x2019;s environment, which is bad. Let&amp;#x2019;s try and get out of that.\n
  • #40: NSNotificationCenter is a good example of a singleton that app code often makes use of. Singletons encourage &amp;#x201C;ask, don&amp;#x2019;t tell&amp;#x201D; because it&amp;#x2019;s easy to grab the shared instance from anywhere. The problem is that this means relying on the code&amp;#x2019;s environment, which is bad. Let&amp;#x2019;s try and get out of that.\n
  • #41: NSNotificationCenter is a good example of a singleton that app code often makes use of. Singletons encourage &amp;#x201C;ask, don&amp;#x2019;t tell&amp;#x201D; because it&amp;#x2019;s easy to grab the shared instance from anywhere. The problem is that this means relying on the code&amp;#x2019;s environment, which is bad. Let&amp;#x2019;s try and get out of that.\n
  • #42: Statement of the problem.\n
  • #43: This is the best solution. Another approach is to swizzle the -defaultCenter method to return your mock. That&amp;#x2019;s a bit dangerous: though test code isn&amp;#x2019;t production code so if it works for you, do it. Beware the pitfalls though.\n
  • #44: \n
  • #45: \n
  • #46: \n
  • #47: If I missed any recipe you would like to have seen, let me know and I&amp;#x2019;ll write it up.\n