Getting started
First steps: Using a simple Stub
The simplest use case for OCMock is the creation of stub objects, that is objects that are set up to return pre-determined values for specific method invocations.
The example
To make this more concrete let's assume we are writing a little application that receives tweets from Twitter. We have a controller class, which for the purpose of this example we just call Controller
, a class that handles the calls to the Twitter API, called TwitterConnection
, and a class called TweetView
that can display tweets.
The controller has references to the connection and the tweet view.
The interface for TwitterConnection
has a method to retrieve new tweets. It returns an array of Tweet
objects, or nil
if the request could not be handled.
The interface for TweetView
has a method to add individual tweets to the view.
Why use mocks for the test
When writing a unit test for updateTweetView
we have to decide what to do about the controller's dependencies, the connection and the view. In the case of the connection we could instantiate a real TwitterConnection
and use that. This would lead to a few problems, though:
- Using a real connection would make the test slow.
- We never really know what Twitter returns at any given moment.
- It is hard to test the error cases because Twitter usually does not return errors.
The solution to these problems is to replace the connection object with a stub.
Introducing a stub for the unit test
Using OCMock we can create a mock object for TwitterConnection
and write the test like this:
The test creates a controller and a mock connection. The controller doesn't know that the connection is not a real connection. The mock connection has a stub for the fetchTweets
method, which is set up to return an array with a test tweet.
We can now begin to implement the updateTweetView
method like this:
When running the test it calls updateTweetView
on the controller, which in turn calls fetchTweets
on the connection. The connection is our mock, which returns the array with the test tweet. So far, so good.
Next step: Verifying interactions with a mock object
In contrast to stubs, which simply provide canned answers, mocks are used to verify interactions. In our case mocks come in handy when we want to ensure that the controller makes the calls to the view.
Expanding the unit test with a mock
Returning to the test we add a few lines to set up a mock view and to verify the controller's interaction with it:
The creation of the mock view is no different from the creation of the mock conntection above and, again, the controller doesn't know that it is connected to a mock and not a real TweetView
instance.
The call to updateTweetView
remains unchanged, too. After it, though, the test verifies that the addTweet:
method was actually invoked, presumably as a result of the test invoking the updateTweetView
method.
In the verify the test uses [OCMArg any]
to tell the mock object that addTweet:
can be called with any argument. It doesn't matter what tweet object is passed.
We can now extend the implementation of updateTweetView
like this:
With this implementation the test passes. If we remove the addTweet:
call from the updateTweetView
method then the test will fail, reporting that an expected method was not invoked. In Xcode and AppCode the error is reported on the line of the verify.
Improving the test with argument matching
This test can be improved. As written above it passes as long as the controller invokes the addTweet:
method. The controller could pass a wrong tweet, or even nil, and the test would still pass. To improve this we use a simple argument constraint by modifying the verification:
This is the simplest argument constraint. The mock object compares whether the object that is passed in the verification is equal to the object that was passed when the method was invoked. Now the verification will fail unless the method is called with the right argument.
Apart from the any and equal constraints shown so far OCMock provides a whole set of different constraints. Stubs can also be set up not only to return values but to throw exceptions or post notifications. All of this is described on the reference page.
Further reading
- Check out the reference documentation and tutorials.
- Read Martin Fowler's article on why Mocks aren't stubs. OCMock supports both, stubs and mocks.