iOS Model-View-Controller TDD [Screencast]

March 7, 2013 — 61 Comments

The UIViewController TDD screencast ended with all the code in the view controller. Unfortunately, this is where many iOS programmers leave things! Let’s TDD our way to something better…

In part 2, we pick up from there and TDD to extract a model class, which the controller observes. You’ll see it evolve into true Model-View-Controller, driven by unit tests.

In particular, you’ll see how to TDD:

  • a model that posts notifications when it changes, and
  • a controller that observes those notifications.

Links:

Question: What difficulties do you face when trying to TDD your code? Leave a comment below.

“Do it now” sound by freesound

Disclosure of Material Connection: Some of the links in the post above are “affiliate links.” This means if you click on the link and purchase the item, I will receive an affiliate commission. Regardless, I only recommend products or services I use personally and believe will add value to my readers. I am disclosing this in accordance with the Federal Trade Commission’s 16 CFR, Part 255: “Guides Concerning the Use of Endorsements and Testimonials in Advertising.”

Enjoyed this article? Sign up to get future articles by email.

Email Address:

61 responses to iOS Model-View-Controller TDD [Screencast]

  1. Another great screencast thanks!

    Just one question. Please could you let me know where you got your code snippets from? (i.e. for properties, etc…) or did you create them all yourself?

    Thanks again!

    • I made them myself. You can, too! For example,
      @property (nonatomic, strong) <#type#> *<#variable#>;

      The testing-specific ones like Test UIButton Action are available to anyone who subscribes, as a thank-you.

  2. Christian Rasmussen March 8, 2013 at 8:00 am

    Thanks for a very helpful screencast!

    What is your opinion on using key-value observing instead of the notification center to notify the view controller?

    • Alternatively to KVO and notification, would you use delegation for something like this?

      I can see all three as working correctly but not sure when/why to use each of the three.

      • Oliver,

        If you only use the NSKeyValueObservingOptionNew option, there’s no real difference between KVO and notification. Well, except for the complaint I shared with Christian.

        The main difference between these and delegation is that delegation is 1:1. The number of delegates is either 0 or 1. But the number of observers is unlimited. For many models, it’s quite conceivable that you could have more than one interested party. I wouldn’t limit it unless the design clearly calls for a 1:1 relationship.

    • Christian, if you like KVO, go for it. It’ll work fine.

      But me, I’ve never cared for KVO. The reason is that observation comes in through a single entry point. If you’re observing multiple things, you have to do your own dispatching based on the key path.

  3. Richard Buckle March 8, 2013 at 5:22 pm

    Great post: thank you for this series!

    A niggle: around 44:00 you modified the meaning of testInitialCountLabelShouldBeZ­ero without renaming it accordingly. That could be *very* confusing to maintenance programmers!

    • Guilty as charged! I realized my error right after finishing everything up, and decided it wasn’t worth redoing. It should really be something like “testCountLabelWhenViewAppearsShouldReflectModel”.
      Thanks for paying attention!

  4. Why not create and dependency-inject a mock notification system? I started wondering this when you set up state in the test to keep track of received notifications (quantity, which seems like an easy target for mock verification, and content, which might not be).

    Is this a matter of “don’t test your libraries – they already work as documented”? An important step to make sure the ‘did change’ notification comes after the change happened?

    • Bryant, I’m not sure what you’re suggesting. Do you mean putting my own wrapper around notifications?

      I’d rather not mock anything that I don’t have to.

  5. Thanks for this latest instalment.

    I’ve been TDD-ing, to some extent, for a while, but haven’t entirely taken the ‘do the simplest thing that could possibly work’ axiom 100% to heart. It’s interesting to watch your baby-steps in this video. Is that really how you code, in daily practise, in your own work?

    As an aside, having seen you tweet about Kiwi a couple of times lately, I’d be interested on your take on that, given your deep familiarity with TDD tools (OCHamcrest/OCMockito work). From what I’ve seen so far (trying Kiwi in a small new project), I like the Kiwi assertions style (they read nicely), but the matchers don’t seem to be as powerful as Hamcrest. I also don’t find Kiwi specs to be quite as readable ‘in the large’ (ie. looking over an entire file of them). This is partly a matter of tool support (eg. lack of quick navigation in AppCode), partly that the specs aren’t as visually familiar as regular classes-and-methods. Also you end up with quite a mess of brackets in Kiwi. But the nested contexts are a joy, particularly given how the latest AppCode EAP displays them so nicely in its test runner.

    • Cris,

      I too have been TDDing to some extent for a while. It’s been only recently that I discovered the importance of the “simplest thing.” The one example in the screencast that I might skip in my work is the test that the controller is observing the correct model. That seems like something that’s unlikely to be accidentally changed the other way. But for everything else, yes, this is how I work. It’s an important ingredient for TDD because it forces your tests to be robust, while striving to keep the code doing only what the tests have promised.

      On Kiwi: The nested contexts are what make me really want to use it. You’re right, the matchers aren’t as powerful. But with a recent fix to Kiwi, you can use OCHamcrest matchers in Kiwi now! Give them a try; they may look a little out of place, but they should work fine.

      • Right. I’ll experiment with TDDing in tiny steps. In principle, I like the idea of the code being absolutely as simple as the specification (ie. the tests) requires, with additional complexity emerging from new specifications. I just haven’t, in practise, found myself quite willing to make minimal steps when I ‘know’ in advance what a couple of the next steps are going to be.

  6. Very helpful screencast, as usual inevitably leading to a few questions on my part.

    At around 9 minutes in, you mention in passing that @class Whatever is better than #import “Whatever.h” in a .h file. Umm, why is that ?

    Secondly there are a couple of places where you write assertThatInt(<int expression>, is(equalTo(@2))) or @1 etc. @number is the new NSNumber syntax yes ? I recall a similar moment when you had assertThatInteger(<NSInteger expression>, is(equalTo(<NSNumber expression>)))

    …this has come up for me before and I’m just about to start TDDing another component in the spirit of open minded experimentation. Could you devote some time to going over the basic mechanics of the various assertThat macros please.

    • I’m not Jon, but I can chime in on a couple of things here.

      @class prevents compiler errors due to circular dependencies. If ClassA’s header #imports ClassB, and ClassB’s #imports A, then neither class can be compiled. That’s the simplest case, but similar dependencies can also arise from more complex chains of references, so might not be obvious. If you just get in the habit of using @class where possible, you don’t need to think about whether or not there’s a circular dependency hidden amongst your classes.

      The assertXXX macros are well-covered in the OCHamcrest docs. They’ll also autocomplete in Xcode/AppCode when OCHamcrest is used in a project. Broadly assertThat(value, matcher) needs objects for both value and matcher. A bunch of assertThat variants allow you to compare values without messing about with un/boxing.

    • Andy,

      On #imports: Cris gives one explanation, but my reasons go beyond breaking circular dependencies. See my post #imports Gone Wild! How to Tame File Dependencies.

      Yes, @1 is the new NSNumber literal.

      assertThatInt(value, matcher) is shorthand for assertThat([NSNumber numberWithInt:value], matcher). Variants exist for all primitive numeric types.

      The most friendly introduction to OCHamcrest is a blog post by Justin Shacklette, Intro to OCHamcrest.

      …Does that answer your questions?

      • Oh! I just discovered, through someone else’s code, that we can ditch assertThatInt and its kin altogether using assertThat(@(value), matcher). I haven’t decided if I like it in context, but it’s pretty neat.

        • Thanks for both sets of replies, and the pointers to the documentation. I find these second order questions on languages (prefer X to Y, although both are legal) endlessly fascinating – but it’s the reasoning behind it that is always the most revealing.

  7. Can you explain why you use Int, Integer and NSInteger in your test. Whats the difference?

  8. Gijora Dammann March 22, 2013 at 2:07 am

    Jon your stuff is great, it helped me alot to get started with tdd.

    Still I have a couple of problem i can´t figure out.

    How do i stub a class method?
    I have tried the following but it didn´t work as expected:

    NSArray *mockParameter = @[@”a”,@”b”];
    NSArray *mockReturnValue = @[@”A”,@”B”];
    [given([SomeClass someClassMethodWithReturnArrayValue:mockParameter]) willReturn:mockReturnValue];

    Also how do i mock a singleton object?

    SomeClass *mockObject = mock([SomeClass class]);
    [given([SomeClass sharedInstance]) willReturn:mockObject];

    The singleton is created like here.

    Maybe i´m on the wrong track here, if you or someone else could give me a heads up, that would be great.

    • Gijora,
      To mock class methods in OCMockito, use mockClass to create a mock class object:

      Class someClassMock = mockClass([SomeClass class]);
      [given([someClassMock someClassMethod…etc…

      You’ll then have to inject the class.

      For singletons, I have to ask: Is this the singleton creation code under your own control, or is it in someone else’s library?

      • Gijora Dammann March 25, 2013 at 2:48 am

        Hey Jon,

        thx for your quick and helpful reply.

        Yes the code for creating the singleton is under my command :-)
        Let me know what you think…

        • In that case, the best advice I have regarding singletons is DON’T. Every use of a singleton locks down dependencies. For unit testing, they usually have to be replaced with some kind of dependency injection. So you may as well start by injecting these objects in the first place.

          The next best advice I have: If you do use a singleton, don’t make it an enforced singleton. Specifically, don’t use the technique linked. It makes it impossible to create and destroy instances of that object in tests. With a loose “shared instance” type singleton, the tests are still free to alloc-init an instance, and destroy it. With a strict singleton, you can’t do that.

          Did that make sense?

          • Hi Jon,

            Is you reason for not using singletons purely for uint testing? For example why would you use anything other than `[NSNotificaitonCenter defaultCenter]` outside of unit test land?

            My only issue with this is that it seems to be enforcing a changing the interface just for the sake of the tests. Why not simplify the interface by allowing global class method mocks like in OCMock & Kiwi?

            Cheers,
            Rob

            • I prefer to make my dependency injection explicit. But I’ve also come to prefer property injection for things that have “suitable defaults” like NSUserDefaults, with a lazy-loaded getter providing the default value.

  9. Hi Jon –

    I just finished watching your 3 screencasts. Awesome stuff! I think you do a great job explaining things & plus you make me feel like I”m on the right track: mentioning uncle Bob – I’ve been reading his things too.

    I’ve inherited the codebase of a rather sloppy developer. After watching your screencasts, I’m considering a rewrite, TDD style. However… other schools (Joel Sopolsky) recommend against a rewrite. Maybe I’ll just write some tests and then refactor.

    I’m looking forward to seeing the TableViewController video.

    BTW, I’m curious… Why do you use XCode in your screencasts? Do you find it runs tests faster?

    Cheers!
    Matt

    • Matt,
      Thanks for the kudos!
      Uncle Bob is good to read, but even better to watch. I highly recommend his video series.

      As for rewrite vs. refactor, I’d start with refactoring. You will have to write tests, but unfortunately tests written after-the-fact are really characterization tests (that is, the tests are written to match the code) rather than describing desired behavior. Still, you can get a long way. Once you’re able to partition the code into sections behind clean interfaces, then you can rewrite those sections in isolation without having to do a complete rewrite.

      As for why I use Xcode… you mean instead of AppCode? Partly because I didn’t start using AppCode until after I started the screencasts. Partly because everyone has Xcode. And partly because I don’t want to embarrass myself with my beginner-level lack of AppCode dexterity! But I’ve actually started thinking about making an AppCode screencast to show how AppCode’s workflow supports TDD. Hmm, maybe I’ll do a short head-to-head comparison.

      • I’ve been using AppCode for a little while, but would really be interested in watching you use it. I’m sure I’d learn several new things. You might wait a bit. The 2.0 they just shipped seems a bit buggy. On my install, searching in the project doesn’t seem to work very well. And so it also wasn’t finding function usages & so simple rename refactorings were not working 100%. May have just been me though — this stuff worked before and yesterday was a bit of a miss for me.

        Thanks for your answers to my questions.

      • I think that a screencast showing a TDD workflow in AppCode is a great idea. I’ve found AppCode to be a much better environment than Xcode for testing in general, and TDD specifically for a few reasons:

        1) The visualization of test status is much better; test start/end and test status are much easier to see.
        2) It’s very easy to run individual test classes and even single tests in isolation.
        3) I find that the refactorings available in AppCode are MUCH better than what is available in Xcode.

        • Yes! It took a little time for AppCode to grow on me. But once I gave it a chance, I discovered that the workflow lends itself to TDD in ways that are hard to imagine if you haven’t seen it in action.

  10. One more question!

    What do you think about using Objection DI in a test to provide the SUT with it’s dependencies?

    overkill? just a bad idea? or just fine?

    Thanks!
    Matt

    • I haven’t felt a need yet for a DI framework to support my dependency injection. So far, factories handle everything I’ve needed.

      If I were to use a DI framework, I’d use Objection. But I’d keep it limited.

      • Aha. So when a class needs to create objects with shorter lifetime, you pass in a factory to the initWith of that class?

        • Yes. Specifically, I pass in an object that conforms to a factory protocol. That way, it’s easy to make a test-specific factory and inject it instead.

          The other way I’ve done this before is to pull the object creation into a method. Then the test can subclass and override that method.

          The factory approach takes a little more work, but is cleaner & more thorough.

  11. Hi Jon –

    I’m just now building out my app using TDD. Being so common, the first screen I wanted to TDD is a UITableViewController.

    In this MVC TDD screencast you mention your next screencast may cover how to TDD a UITableView. Any chance you could give a few spoilers?

    I’m interested to know, just briefly, what sorts of things you mock when building out a TVC.

    Thank you!
    Matt

    • Matt,
      My short answer on what I mock for table view controllers is “not much.” That’s because I usually inject the model. Let’s say I create a model of 2 items. Given that model, the tests can state:

      • how many rows should be shown
      • what the content of each row should be
      • what should happen when the user selects a row

      See?

  12. Hi John,

    highly appreciate the work you (and others too) did for a lot of us. I started with OCUnit a year ago but got stuck with resolving dependencies issues and just found your site and your screencasts by chance. Anyhow I not quite sure how to tackle the following.

    I’ve got an NSObject that has a delegate protocol and I want to make sure that the delegate can’t be set to an object which doesn’t conform to the protocol (throws an exception), nil is accepted though.

    Here is what I did so far and it works, but is there another way using OCHamcrest?

    - (void)setDelegate:(id)delegate
    {
        if (delegate && ![delegate conformsToProtocol:@protocol(DelegateProtocol)]) {
            [[NSException exceptionWithName:NSInvalidArgumentException
                                     reason:@"Delegate does not conform to DelegateProtocol protocol"
                                   userInfo:nil] raise];
        }
        _delegate = delegate;
    }

    - (void)testManagerShouldRaiseExceptionIfDelegateDoesNotConformToProtocol
    {
        // given
        id delegate = [[NSObject alloc] init];
       
        // then
        STAssertThrows(self.mgr.delegate = delegate, nil);
    }
    • Hi Bernd,

      Since you’re requiring your delegate to conform to a protocol, just type it explicitly:

      @property (weak) id <DelegateProtocol> delegate;

      It’s not only super-simple, but the feedback is immediate because the compiler will complain. No unit tests necessary.

  13. Should have said that I try to follow the sample app in “Test-Driven iOS Development” from Graham Lee but using OCMockito/OCHamcrest.
    Anyway thanks for your reply and I agree about what you wrote about the book.

    • Ah! If you really want to make a runtime guarantee, then I’d use STAssertThrows as you show in your example. OCHamcrest doesn’t know how to catch exceptions.

  14. Thanks for the great screencast. I had to restart a very old app anyway so I thought I would start with TDD. As I was running through the process for writing tests for the UILabels I kept getting into an issue because the viewcontroller is a part of a storyboard. I found an answer here:
    http://yetanotherdevelopersblog.blogspot.com/2012/03/how-to-test-storyboard-ios-view.html
    I thought it might help others.

    In xcode After tagging the storyboard appropriately I had to delete the app from the simulator and do a clean on xcode for it to finally work. Now the tests actually pass! Unfortunately on AppCode it still gives me an error of:
    Storyboard () doesn’t contain a view controller with identifier ‘MyView’

    Not sure why this would be since it builds and tests fine in xcode?

    • Prasanth, sometimes the simulator needs to be cleaned out for it to get your latest resource changes. Try resetting it, but then use AppCode instead of Xcode and see if that works.
      If it doesn’t, report the issue to JetBrains.

  15. Hi Jon,

    Great stuff! Not sure if you know, but someone did a follow up to your screencast. http://www.youtube.com/watch?v=Dog6zAz2NIA

  16. Thanks for this very elegant solution to TDD for ViewControllers. I have found all your screen casts to be very helpful.

    I am now looking into how to TDD the storyboard segue to the next ViewController. One potentially nice way to do it is described by Bernd Rabe on StackOverflow but I’m having trouble with the objc_getAssociatedObject part (mainly where to set/get the storyboardSegueKey).
    The basic concept is:

    1. test the existence of the segue (is it hooked up to the view controller under test)
    2. force the view controller to trigger its performSegueWithIdentifier and intercept the prepareForSegue method to see if everything for the next view controller is set up. (This is done with method swizzling)

    Anyway, have you done any TDD related to segues before? If so would it be possible for you to describe the basic concept either in a blog post or preferably another excellent screencast.

  17. Hi Jon,

    Your screencasts for test driven development have been very helpful. Do you have a github site or place where you display the code for this MVC TDD example? That would be incredibly helpful.

    • Kyle, I haven’t put the code up because I want people to see the process, not just a snapshot. But I do hope to make an example to demonstrate clean code, particularly how to avoid making a “massive view controller.”

      • Although I understand this educational point of view, it’s quite hard to click through the video just to find out what code you end up using. Some kind of reference would be nice.

  18. Excellent tutorial! When is the mentioned episode focusing on UITableViewController to be expected?
    Would you use NSNotificationCenter for showing/displaying (inserting/removing) cells in a UITableView, too?

  19. Oliver Brzoska April 3, 2014 at 2:43 am

    Hi Jon,
    this is such a great tutorial, it finally got me really getting started with TDD! Please don’t abandon your screencasting plans! You ‘promised’ the next episode more than a year ago…
    I’d love to see you doing a ‘more advanced’ screencast with e.g. asynchronous things, networking (e.g. with Parse), mocking Core Data access, etc. (or at least one of these things as an example).
    And of cause I’d love to see if your workflow changed in Xcode 5. (Or did you really switch to AppCode completely?)
    Thank you!

    • Oliver, I guess I should get back to making screencasts, but in the meantime, a couple of quickies:
      – For Core Data, I’ve used an in-memory store.
      – Yes, I use AppCode all the time now when possible. It’s so much better for TDD!

  20. I have a question about asynchronous notification TDD. Such as your screencast, when app started, maybe send request to web server and receive some data, in the end post notification from Module.
    The request and receive data may be have 1 or 2 or more seconds, how to test case this notification?

    • Well… I found a function to do like this:

      - (void)setUp
      {
          [super setUp];
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(responseGetMenu:) name:REQUEST_MENUS object:nil];
      }

      - (void)testGetMenu
      {
          [[DataEngine sharedDataEngine] getMenu:@"..."];
          [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
          XCTAssertTrue([[DataEngine sharedDataEngine].menus count] > 0, @"");
      }

      - (void)responseGetMenu:(NSNotification *)notification
      {
          NSLog(@"MENUS:%@", [DataEngine sharedDataEngine].menus);
      }
    • Yes, as you’ve found, you need to use a runloop with a timeout. But it is much slower than a pure unit test. I usually place such tests in a different test target, so that I can run “fast tests” on every TDD 3-step cycle, and “slow tests” less often.

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!