Intro to Objective-C TDD [Screencast]

December 29, 2012 — 62 Comments

My first screencast! It was sparked by a Stack Overflow question that said, “All the examples of unit testing I read about seem to be extremely simple and trivial.” The question asks how to write unit tests for a piece of sample code. What’s interesting about this problem is that it uses NSUserDefaults.

I cover:

  • What to do with an external object (in this case, the NSUserDefaults singleton). Dependency injection to the rescue!
  • My basic setup: OCUnit + OCHamcrest + OCMockito (along with my test case template and test code snippets)
  • The 3 steps of “The TDD Waltz”
  • We TDD our way to the desired functionality

Any questions? I’d love to hear your experience with this screencast. 

Links:
  • The Stack Overflow question. My original answer illustrates setter injection, as opposed to the (better) constructor injection of the video.
  • My Resources page, where you can get OCHamcrest, OCMockito, and my test case template
  • Subscribe to this blog and I’ll send you my test code snippets
  • AppCode, an alternative IDE with much more automated refactoring than Xcode
  • Clean Coders video series by “Uncle Bob” Martin

See also: UIViewController TDD [Screencast]

Jon Reid

Posts Twitter Facebook Google+

I'm passionate about not just improving our code, but improving the way we code.

62 responses to Intro to Objective-C TDD [Screencast]

  1. Thanks a lot Jon!
    This is exactly what I needed, you are very clear and the way you share your knowledge is really wonderfull.
    Gracias!

  2. Nice, clear screencast. I’ve been using OCHamcrest for a while (for which you have my gratitude), but hadn’t come across OCMockito. Looks good — I shall definitely give it a test run.

    One question: how do you feel about the ‘only mock types you own’ rule of thumb? It seems to me to be difficult to follow in a Cocoa context, because idiomatic Cocoa code is shot through with uses of Core/Foundation classes.

    From your example I’d guess you might think the same — eg you’ve mocked NSUserDefaults rather than driving out a domain-oriented protocol to be later implemented with NSUserDefaults (which might be the standard “mockist” approach). I’d be interested in your thoughts on this.

    Having not used Xcode much for a while, I’m also struck by how nice AppCode is for TDD. Not only the refactoring support, but also the ability to introduce new class names in code, and just tell AppCode to create the new class. It also has a much nicer test runner than Xcode’s.

    • Crispin,

      Glad you like OCHamcrest! OCMockito is actually written on top of it: every verify you see in the screencast actually uses an implicit equalTo matcher. So you can specify any matcher.

      Regarding “only mock types you own”… I’m embarrassed to say I’ve never heard of this rule of thumb! (I have yet to read the GOOS book.) But now that I see what Steve Freeman says about it, I can see how it would lead to extracting a class for “persistent store for the reminder ID” where that persistent store could be implemented any number of ways.

      But once you get down to TDDing that class, you’d still need a way to mock out NSUserDefaults or Core Data or whatever you use. At some point, you still need to talk to Cocoa.

      One thing I haven’t figured out with AppCode is how to set up a three-panel view so I can see the test, .h and .m in one shot. Two of them, yes, but not three. Any hints on this, or shall I file a feature request with JetBrains?

      • I have read Freeman’s book, but it was my intro to TDD, and although it’s an easy enough read, there was too much in there for me to practically absorb in one go.

        His “mockist” approach seems to me a dubious fit to iOS/Mac dev. You’d end up with a vast number of protocols/classes intervening between your domain classes and Apple’s frameworks. It seems more natural to me to think of the frameworks as being the language/environment you’re working ‘in’ (rather than to be isolated ‘from’ via connectors), and use mocks primarily for stubbing the DI’d dependencies.

        Re AppCode — yes, you can set it up to have 3 panels (just tried it). First do a vertical split, then highlight the RH and do a horizontal split. The splits can be got at either via context menu on the tab, or just CMD-SHIFT-A and search for ‘split’.

        • Yeah, while I appreciate the way the “mockist” approach drives design, I guess I’d call myself a “pragmatist”: I’ll do whatever helps me move forward. Maybe that’s “classicist” after all.

          Thanks for the AppCode tip!

  3. Hi Jon,

    Thanks so much for this really helpful. A few questions:

    1) I live in AppCode and rarely use Xcode for anything other then InterfaceBuilder/StoryBoard. Are the frameworks and templates applicable there?
    2) While I am a committed unit tester I remain baffled by a rather fundamental issue: how do I run a unit test without the appDelegate and root container viewController callbacks firing. Presumably this is a mock object issue? I stumped.

    Anyway, thanks for all your super helpful tips.

    Cheers,
    Doug

    • Thanks, Doug.

      1) AppCode supports “New File from Xcode Template” so they’ll work just fine. And the framework I use work fine. Only the Xcode 4 code snippets don’t transfer. I’m making matching live templates but still need to figure out where AppCode puts them.

      2) That’s a good question, deserving of another blog post or short screencast. In the meantime, I explain my approach in an answer to the Stack Overflow question, “How can I keep my app from creating a viewcontroller when running unit tests?”

      • My man! Cool. Thanks. Have a Happy New Year.

      • A different way to avoid any of the app framework stuff firing is to run what Apple call “logic tests” (link).

        When you check the ‘unit tests’ checkbox when creating a new project, the unit test target Xcode creates has the BUNDLE_LOADER and TEST_HOST build settings pointing to the app bundle. This makes the unit tests ‘Application tests’, ie. they’re injected into the running app.

        You can make a Logic Tests target (with BUNDLE_LOADER and TEST_HOST both unset) by adding a new unit test target to the project. Tests added to this target will run within a standalone test host script, and won’t fire any of the UIApplication framework delegate methods.

        Xcode is a bit misleading here, because even when running logic tests only it will launch the iOS simulator if it’s not already running. But it still runs the tests as logic tests (I’ve tested this in 4.5.2 — breakpoints in the app delegate aren’t hit). AppCode behaves as expected (only launching the simulator for application tests).

        • Crispin, I never liked “logic tests” vs. “application tests” because it forces me to categorize tests according to whether or not they need the iOS runtime. (See my post Xcode Unit Testing: The Pros & Cons of Xcode 4.) Besides, for iOS, quite a bit of any app is view controllers. I just want to hit ⌘U and have everything run.

          • Crispin Bennett January 2, 2013 at 6:57 pm

            Jon — I can see that. Logic vs application tests is an odd distinction anyway, seemingly unique to Apple. OTOH, it’s a bit ugly to have to put test-specific code in the AppDelegate (as per your SO post).

            So you do TDD (or at least unit testing) on View Controllers? I admit I don’t (despite having read Graham Lee’s book). I tend to keep my controllers extremely thin, and push logic out to supplementary objects.

            • It is ugly. But it gets the job done, and lets me TDD my view controllers. For example, this lets me verify that row N in a table view is showing what I want. This might be row N from the model, or it may be more complicated when the table includes empty spacer cells and special divider cells.

  4. FYI – latest install doesn’t include the command line tools but you can use the following instead;

    xcrun make install

    (or install the tools manually)

  5. Thanks a lot for this excellent screencast, Jon. I didn’t know your site, but certainly will be in my bookmarks from now on.

    Cheers!

  6. Thanks for the great screencast Jon. I hope you do more!

  7. Very awesome screencast. I’m familiar with the concepts (DI, TDD, etc) from Java and other languages, but appreciate you going through the testing libraries and how the patterns are done in Objective-C. I’m still trying to figure out idiomatic ObjC (for example, the “shared singleton” pattern), rather than squeezing my Java patterns in here.

    For instance, I’m writing an API wrapper that has a public API which then delegates to an internal ConnectionFactory to create a Connection which wraps the actual Objective-C networking code. This is the way I’d do the connector in Java (e.g., Android), but not sure its the right/idiomatic way to architect an iOS library. If you have any insights, I’d appreciate it.

    Anyway, enough rambling. Thanks again for the wonderful screencast.

    • Cody, if your wrapper does more than delegate the call (marshaling things in some way), then it sounds fine. But if your API is identical to the underlying API, then I wouldn’t bother wrapping it; just inject the underlying object or class.

  8. Thanks for a brilliant screencast, John! I’m really looking forward to the next one – any ETA on that?

    I’m just getting into TDD, having done iOS development for a number of years. I’m still struggling with some of the basic ‘how do I test this?’ issues, and so these kind of videos are really useful. As the original StackOverflow question said, it’s hard to find good non-trivial real-world examples that aren’t just adding a few numbers together with no UI (so I’m especially looking forward to your viewController example!)

    I’m trying to blunder my way through TDDing a super simple App.net global timeline viewer. It seemed like a nice small test project with limited scope but that does a number of things I’d do in a ‘real’ app – namely downloading data (using AFNetworking), perhaps persisting it, and displaying it in a tableview. Testing the ViewController and the asynchronous block-based networking calls are my biggest stumbling points, so if you need any inspiration for future videos…!

    • Thanks, James! The introductory View Controller TDD video is cooking as we speak. I hope to get it up in a day or two.

      And thanks for the good, typical examples. The next ones I’m planning are:

      - Model-View-Controller: How to TDD beyond lumping everything into the view controller
      - Table views

      Networking is a nice one to add to the list, for sure.

  9. Great screencast! Thanks a lot for teaching best practices, Jon! Definitely adding your website to favorites ;) More screencasts on TDD will be appreciated.

  10. John, thank you so much for this screencast (and the follow-ups). I have the feeling that the Objective-C community is somewhat afraid of TDD because there are so many different ways test and it’s hard to find authoritative sources out there. On top of this, the Apple documentation is very formal and assumes too much existing knowledge of TDD, while the defaults often steer us in the wrong direction, as you accurately point out in the video. Screencasts like these are great for wrapping your head around the concepts, while simultaneously getting a few bites of best practices. Thank you so much, please keep them coming! Cheers from Amsterdam.

  11. Hi Jon,

    I’ve tried using the OCMockito framework with an iOS project but the build fails due to armv7s stuff.

    It works fine if I use a static cocoa library though.

    Is there something I need to change to get this to work or is it not intended to be used in that way?

    Thanks again

    Oliver

    • Well, I don’t understand. I got home and tried again on my personal MacBook and it now seems to work.

      Not sure what’s happening at work?!

      • Oliver, the packaged release doesn’t have armv7s. Either build from latest sources (with ./MakeDistribution.sh) or — what I do — don’t run unit tests on device. I always use simulator, for sheer speed.

        • Of course!

          I will try again tomorrow.

          Thanks for you help and patience with me. I’m struggling through the very beginning on my own but getting there bit by bit.

          Thanks again for your help.

        • Great Webcast Jon! Best one I’ve seen on TDD. Btw, for the building for arm7 issue, will you be reposting for armv7 support or should I rebuild myself. If I rebuild myself, will it generate a nice framework package that I can just drop into Xcode frameworks.

          Thanks

          • It always surprises me that people are using OCMockito on devices. I always run unit tests on simulator, for speed.
            …That said, if you build it yourself according to the instructions, you do end up with a framework you can just drop in.

  12. Thank you for a enlightening screencast!
    Very good point to start out with TDD…

    Will suscribe to be on the loop!

  13. Thank you for this lecture.

  14. Thanks for this screencast! It’s fantastic to start out with TDD, however I think I am still need some guidance in Spanish (my fault)… I am taking a course on TDD (for Spanish speakers) Thanks for your help.

  15. Hi Jon -

    Is it possible to use OCMockito/Hamcrest to verify a method call with a block parameter?

    I do not need to inspect the value of the block parameter, however a call like this does not build:

    [verify(restFacade) getUploadUrlWithCompletionBlock:anything()];

    Build Error:

    sending ‘id’ to parameter of incompatible type ‘void (^)(KPUploadURL *__strong, NSError *__strong)’

    Or should I stop using blocks in my code (are they harder to test?)

    Thanks so much for this great page & for the very useful tools that you write.

    Matt

  16. Hi Jon, this looks like a really useful library, unfortunately I cant get it going in my project, after importing the frameworks I’m just getting a bunch of build compiler errors :

    Undefined symbols for architecture i386:
    “_llvm_gcda_emit_arcs”, referenced from:
    ___llvm_gcov_writeout in OCMockitoIOS(MKTObjectMock.o)
    ___llvm_gcov_writeout in OCMockitoIOS(MKTMockitoCore.o)
    ___llvm_gcov_writeout in OCMockitoIOS(MKTMockingProgress.o)
    ___llvm_gcov_writeout in OCMockitoIOS(MKTExactTimes.o)
    ___llvm_gcov_writeout in OCMockitoIOS(MKTVerificationData.o)
    ___llvm_gcov_writeout in OCMockitoIOS(MKTInvocationContainer.o)
    ___llvm_gcov_writeout in OCMockitoIOS(MKTException.o)

    I followed the steps on the giithub page

    cloned the github repo (into no particular folder)
    From the dir I followed the instructions :
    $ git submodule update –init
    $ cd Source
    $ ./MakeDistribution.sh

    The logs printed to the console indicated a successful build

    In my project I went to the Project settings -> projectTest (Target) ->Build Phases

    I imported both the frameworks by going to link binary with libraries -> + -> add other… -> selected OCMockitoIOS.framework and OCHamecrestIOS.framework

    These appeared as frameworks in the list of libraries

    I then moved then to the bottom of the list (to try to get rid of the error)

    Any idea why the frameworks aren’t compiling???

    • Just as an update, following exactly the steps on the screen cast – works. Using the steps on github (clone from git / make install / etc) doesn’t seem to work (errors above). hmmm – not sure why, but at least Ive got it compiling for now!

      • Michael, that was my bad. The MakeDistribution script was building the Debug configuration, which is instrumented for code coverage. I switched it back to Release.
        Thanks for letting me know!

  17. Hello Jon,

    I tried to recap your great screencast for MacOS instead of iOS and run into trouble when I picked OCHamcrest 2.0 instead of the 1.9 which is bundled with OCMockito. I run into BAD_ACCESS errors during runtime.

    Ok, my fault, but I think you should point out this dependency…

    As you mentioned AppCode is great for refactoring.

    Keep on producing such good screencasts on TDD with MacOS and iOS!

    Regards
    Niels

    • Niels,
      I noticed the problem with OCHamcrest 2.0.0 for Mac. I released an update 2.0.1 which fixes the problem.

      Thanks for letting me know the 1.9 was OK. My fix for 2.0.1 was to disable code optimization. I wonder what the problem is…

  18. Thanks so much for a fantastic screencast!

    I thought it was a great touch to do the screencast in the context of a response to an SO question. I’m new to much of this so referencing something familiar brought it that much closer to home.

    ‘lookin forward to the others!

  19. Fantastic!!! Thanks so much!
    -greg

  20. Great tutorial, thanks for sharing. I’m going to listen to the other ones this weekend.

    I do have a question about the number of lines per method, and the urge to keep it small. I understand the reasoning, but there are methods where you just need more lines, eg in

    - (void)drawRect:(CGRect)rect

    or

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    , things like that.

    What would be the benefit to refactor such methods, it makes the code less readable and more difficult to debug when scrolling back and forward between methods?

    • Jon might have a better response than me to this but I’ve always interpreted it as something we strive for rather than something that can be accomplished 100% of the time. Keeping that in mind, when your functions get big it should a red flag to consider doing some re-factoring.

      Cheers

    • Koen,

      The benefits to smaller, well-named methods:
      - readability
      - maintainability
      - self-documenting
      - easier to replace/extract/override parts
      - everything has a single responsibility

      Let’s take some examples. Here’s a cellForRowAtIndexPath I wrote (with some names changed):

      - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
      {
          MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:[self cellIdentifier]];
          [self configureCell:cell forIndexPath:indexPath];
          return cell;
      }

      And here’s a drawRect example:

      - (void)drawRect:(CGRect)rect
      {
          CGContextRef context = [self makeDrawingContextPen];
          rect = [self insetRectForLineAndShadow:rect];
          [self drawBubbleInContext:context rect:rect];
          [self redrawLowerRightForShadowInContext:context rect:rect];
      }

      (I was able to safely refactor the drawRect code because one of my tests is an acceptance test, comparing the drawing against a baseline image.)

      • Thanks Jon,

        I understand it better now and even started to apply this in parts of my code. I think viewDidLoad is also a good candidate to extract code from (setting up the navbar, the views, the toolbar, etc).

  21. Really great video. So nice to hear someone talk about dependency injection first thing. It’s been absent in other “Intro TDD” article’s I’ve read yet seems like one of the most important things to hammer home. I’m fairly confident in my ability to do TDD on server side code, but applications have been a beast to wrap my head around and this has really helped.

    Much like a comment above, I would love to learn more about asynchronous testing in this age of social networking and cloud storage.
    —–

    One thing about the # of lines of code per function that I’ve been unsure about is whether lines within a condition are counted separately.

    That is, let’s assume I have an if/else condition each containing 2 lines. Is that 4 lines of code or only 2 because only 2 lines would ever be executed. Furthermore, does the if statement count as one line itself?

    I tend to only count the maximum # of executions in a function as what it’s total line count is but perhaps I’m not strict enough with myself.

    • Thanks, Kyle. I’ll (slowly) work towards how to do that.

      As far as number of lines: The importance is for reading, not execution. Count each line, whether it’s executed or not. It’ll force you to rethink things! Oh, and also extract the condition part of an if-statement into its own predicate method. That’s not to make things smaller, it’s to give it a good readable name that explains what it’s for.

  22. Thank you very much for sharing your knowledge with this rich material!

  23. Paul Williamson October 17, 2013 at 2:49 pm

    I can’t believe it took me this long to stumble across your blog. IV been trying for a couple of months now to see where TDD fits in with iOS. Thanks for your hard work – now I’m going to watch the view controller screen casts!

  24. Hi John,
    Great Tutorial !! Indeed.
    I was working around the unit Test cases for quite some time and come acrossed The UItableView Deleagte Methods.It would be of great Help if you put some light on Writing the test cases for the Delegate methods,be ity a table view or any other view.

    Thanks in advance.

  25. Thanks Jon for this awesome knowledge you shared with impressive screencast you made.

    Great tutorial.

Leave a Reply

*

Text formatting is available via select HTML.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> 

Have you Subscribed yet? Don't miss a post!