16

Jun

2014

Unit Testing Objective-C Blocks

By: June 16, 2014

Building with Blocks

When developing applications that use frameworks in Objective-C, you often encounter method signatures like:

(From RestKit’s RKObjectManager)

Some of this method signature is as vanilla as it gets, something you might even write in your first Hello World iPhone app. For example, the method “getObjectsAtPath” takes four parameters, the first two are normal, a string and a dictionary.

But what about the last two parameters? What’s that funky ^ symbol mean? In Objective-C a carrot symbol is used to denote a block. Block's are the Objective-C version of an anonymous function, and are used to create closures. According to Apple’s “Programming with Objective-C” guide, blocks are "distinct segments of code that can be passed around to methods or functions as if they were values.” (Programming with Objective-C: Working with Blocks). By being able to pass a block to another method, you are able to add behavior to the other method, without actually modifying that method's code. By using blocks this way, you are able to specify different behavior, on other objects that you may not have access to modify, at run time.

Breaking down the example

Let's look at how that method may appear in a project:

 

In this example, we are using RestKit to make a simple GET request to our server resource “/lists.” We aren’t passing any parameters to the server. We are sending two different blocks to the method. These blocks define two different flows of behavior: the “success” parameter defines behavior for how the application should respond to a successful request to the server-side resource. The other parameter, “failure” defines behavior for how the application should respond to a failed request.

But is that testable?

At AWeber, we strive to unit test as thoroughly as possible. In order to do that, the code under test must be written in a testable way. In the example above, “loadDataFromServer” is not testable code. With the code in that form, it’s very hard to write a test that verifies that the view is updated when the request is successful. The opposite is also true, it’s very hard to write a test that verifies an alert is shown when a request fails.

A pattern for testability

Considering an automated test for this method at a high level, we want to verify that when a request is successful, the view is updated. But, since the method getObjectsAtPath is buried within RestKit, and likely making an HTTP request over the network, how can we write a test that will reliably verify the code in a performant manner?

Here’s the steps we follow to achieve testability:

  1. Refactor the in-line definition of the block into it’s own dedicated method. This method will return a block representing the desired functionality.
  2. Create a property for each unique block. These properties will hold the results of calling the methods created in step 1.
  3. Pass the properties (that hold a pointer to the blocks) to getObjectsAtPath.

Step 1 provides direct access to unit test the code that defines the behavior. Step 2 allows us to manually insert our own blocks from our test case. Step 3 allows us to make an assertion in our test of a method we expect to be called on success (and we can simulate success by mocking the objectManager and setting an expectation that getObjectsAtPath will be called with specific parameters).

Here’s a look at the refactoring:

Here are the tests

After this refactoring, we have all the pieces in place to more easily achieve a much higher level of unit test coverage. There are three tests that need to be written:

  1. Verify that restkitSuccessBlock updates the view
  2. Verify that restkitFailureBlock shows an alert
  3. Verify that loadDataFromServer calls getObjectsAtPath with the correct success and failure blocks

(Note: this example heavily leans on OCMock to help verify expected behavior. See this writeup for an in-depth discussion of OCMock)

There are some details of compiler magic left out of these examples to achieve brevity in writing this post.

Wrap-up

This approach is not perfect. A close eye will see that the conditionals in loadDataFromServer that selectively assign self.restkitSuccessBlock and self.restkitFailureBlock are left unverified. Due to the nature of how methods are defined to create and return a block, we’ve not yet found a solution for verifying that code (suggestions welcome!).

Looking on the bright side though, this design is both more modular and more open for testability.

  • Ricardo Ruiz López

    Do you really have to call stopMocking in testLoadDataFromServer?
    According OCMock docs it will be called automatically every time is deallocated, and in that case will lose scope when ending that method.