blob: 412af68e6b51c9a808d0a5e046460ca285213be2 [file] [log] [blame] [edit]
---
layout: default
title: Getting started
tags: []
status: publish
type: page
published: true
nosidebar: true
---
<h1>{{ page.title }}</h1>
<h2 id="simplestub">First steps: Using a simple Stub</h2>
<p>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.
<h3>The example</h3>
<p>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 <code>Controller</code>, a class that handles the calls to the Twitter API, called <code>TwitterConnection</code>, and a class called <code>TweetView</code> that can display tweets.
<p>The controller has references to the connection and the tweet view.
{% highlight objc %}
@interface Controller
@property(retain) TwitterConnection *connection;
@property(retain) TweetView *tweetView;
- (void)updateTweetView;
@end
{% endhighlight objc %}
<p>The interface for <code>TwitterConnection</code> has a method to retrieve new tweets. It returns an array of <code>Tweet</code> objects, or <code>nil</code> if the request could not be handled.
{% highlight objc %}
@interface TwitterConnection
- (NSArray *)fetchTweets;
@end
{% endhighlight objc %}
<p>The interface for <code>TweetView</code> has a method to add individual tweets to the view.
{% highlight objc %}
@interface TweetView
- (void)addTweet:(Tweet *)aTweet;
@end
{% endhighlight objc %}
<h3>Why use mocks for the test</h3>
<p>When writing a unit test for <code>updateTweetView</code> 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 <code>TwitterConnection</code> and use that. This would lead to a few problems, though:
<ul>
<li>Using a real connection would make the test slow.
<li>We never really know what Twitter returns at any given moment.
<li>It is hard to test the error cases because Twitter usually does not return errors.
</ul>
<p>The solution to these problems is to replace the connection object with a stub.
<h3>Introducing a stub for the unit test</h3>
<p>Using OCMock we can create a mock object for <code>TwitterConnection</code> and write the test like this:
{% highlight objc %}
- (void)testDisplaysTweetsRetrievedFromConnection
{
Controller *controller = [[[Controller alloc] init] autorelease];
id mockConnection = OCMClassMock([TwitterConnection class]);
controller.connection = mockConnection;
Tweet *testTweet = /* create a tweet somehow */;
NSArray *tweetArray = [NSArray arrayWithObject:testTweet];
OCMStub([mockConnection fetchTweets]).andReturn(tweetArray);
[controller updateTweetView];
}
{% endhighlight objc %}
<p>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 <code>fetchTweets</code> method, which is set up to return an array with a test tweet.
<p>We can now begin to implement the <code>updateTweetView</code> method like this:
{% highlight objc %}
- (void)updateTweetView
{
NSArray *tweets = [connection fetchTweets];
if (tweets != nil) {
/* display tweets */
} else {
/* handle error cases */
}
}
{% endhighlight objc %}
<p>When running the test it calls <code>updateTweetView</code> on the controller, which in turn calls <code>fetchTweets</code> on the connection. The connection is our mock, which returns the array with the test tweet. So far, so good.
<h2 id="simplemock">Next step: Verifying interactions with a mock object</h2>
<p>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.
<h3>Expanding the unit test with a mock</h3>
<p>Returning to the test we add a few lines to set up a mock view and to verify the controller's interaction with it:
{% highlight objc %}
- (void)testDisplaysTweetsRetrievedFromConnection
{
Controller *controller = [[Controller alloc] init];
id mockConnection = OCMClassMock([TwitterConnection class]);
controller.connection = mockConnection;
Tweet *testTweet = /* create a tweet somehow */;
NSArray *tweetArray = [NSArray arrayWithObject:testTweet];
OCMStub([mockConnection retrieveTweetsForSearchTerm:[OCMArg any]]).andReturn(tweetArray);
id mockView = OCMClassMock([TweetView class]);
controller.tweetView = mockView;
[controller updateTweetView];
OCMVerify([mockView addTweet:[OCMArg any]]);
}
{% endhighlight objc %}
<p>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 <code>TweetView</code> instance.
<p>The call to <code>updateTweetView</code> remains unchanged, too. After it, though, the test verifies that the <code>addTweet:</code> method was actually invoked, presumably as a result of the test invoking the <code>updateTweetView</code> method.
<p>In the verify the test uses <code>[OCMArg any]</code> to tell the mock object that <code>addTweet:</code> can be called with any argument. It doesn't matter what tweet object is passed.
<p>We can now extend the implementation of <code>updateTweetView</code> like this:
{% highlight objc %}
- (void)updateTweetView
{
NSArray *tweets = [connection fetchTweets];
if (tweets != nil) {
for (Tweet t in tweets)
[tweetView addTweet:t];
} else {
/* handle error cases */
}
}
{% endhighlight objc %}
<p>With this implementation the test passes. If we remove the <code>addTweet:</code> call from the <code>updateTweetView</code> 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.
<h3>Improving the test with argument matching</h3>
<p>This test can be improved. As written above it passes as long as the controller invokes the <code>addTweet:</code> 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:
{% highlight objc %}
OCMVerify([mockView addTweet:testTweet]);
{% endhighlight objc %}
<p>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.
<p>Apart from the <i>any</i> and <i>equal</i> 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 <a href="{{ site.baseurl }}/reference">reference page</a>.
<h2>Further reading</h2>
<ul>
<li>Check out the <a href="{{ site.baseurl }}/reference">reference documentation</a> and <a href="{{ site.baseurl }}/support">tutorials</a>.
<li>Read Martin Fowler's article on why <a href="http://martinfowler.com/articles/mocksArentStubs.html">Mocks aren't stubs</a>. OCMock supports both, stubs and mocks.
</ul>