Patterns

This page contains a list of iOS unit test related patterns and techniques that I use. Some are industry standard, such as MVCS, while others are just some common techniques that I have come to use. It’s quite possible that others have already identified these patterns. If I find out about these, then I’ll rename them to match the names previously given to them.

Simple Init Test Pattern

When testing an object’s init method, assertions can be made on the initialized state of the test object. So for example, if an object’s init method sets a default value of 55 to a speedLimit property, then the class’s unit tests should include a test such as:

- (void)testThatInitSetsSpeedLimitTo55 {
    int speedLimit = self.yourObject.speedLimit;
    STAssertTrue( speedLimit == 55, @"Speed limit %d should be 55",speedLimit);
}

Complex Init Test Pattern

Some object init methods perform complex operations, such as initiating asynchronous tasks that might modify the state of the initialized object. In order to be able to safely test such init methods, the init can be split into two separate  init methods. The first init method should perform only safe default assignments. This init will be used to create the object being tested. The second init method can be used by all product code, and it in turn calls the first init method. Refer to the post on [[MyObject]alloc]Init] testing.

Other Object Init Dependency Test Pattern

Avoid including [[OtherObject]alloc]init] statements in a method containing other code. Instead, move it to a separate helper method. This helper method can then be intercepted using a partial mock.

// Product code that creates another object
- (void)methodThatCreatesAnotherObject {
    self.otherObject = [[OtherObject alloc]init];
}

The above code could be hard to test if the other object has dependencies. Testing this method as it is would actually be unit testing the other object’s init method. We’d be better off doing that in the other object’s unit tests. So instead, let’s break this apart as shown here:

// Product code that creates another object
- (void)methodThatCreatesAnotherObject {
    self.otherObject = [self privateCreateOtherObject];
}
// Private method (put into class extension, not header)
- (void)privateCreateOtherObject {
    return [[OtherObject alloc]init];
}

Now we can test this using a Partial Mock:

- (void)testMethodThatCreatesAnotherObject {
    id mock = [OCMockObject partialMockForObject:self.yourObject];
    [[mock expect]privateCreateOtherObject];
    [mock methodThatCreatesAnotherObject];
    [mock verify];
}

Other Method Dependency Test Pattern

When an object method being tested makes calls to other methods on that same object, use a partial mock to stub or expect the other methods.
The previous example shows how this can be done.

MVCS

This is a somewhat new industry design pattern. I first saw this explained in the Big Nerd Ranch book, version 2. It was subsequently expanded upon in the version 3 book. While not specifically a pattern for unit testing, it addresses the situation frequently found in iOS apps related to interfacing with a host API. The extraction of all API code from the View Controller into a separate Store class greatly simplifies unit testing. I suspect that some of the bad reputation that View Controllers have related to unit testing is caused by having this code combined in them.

Skinny Controllers, Fat Models

Often times it isn’t clear whether a particular function should be implemented in a model or the controller. This industry standard pattern simply recommends choosing the model whenever possible. Models are almost always easier to test than controllers. So simplify your testing by putting as much of the code as possible into the model instead of the controller.

Tell, Don’t Ask

I first heard Graham Lee mention this on his iDeveloper.tv video on unit testing, and again in his Test Driven iOS Development book. This means passing objects into methods (“tell”), instead of having them get (“ask for”) the object themselves. The later is typically done using a create class method. By having the caller create the object and pass it to the callee, unit tests are able to easily replace the object with a mock.

 

 

Leave a Reply