Unit Testing Tutorial: Mocking Objects
In this tutorial you’ll learn how to write your own mocks, fakes and stubs to test a simple app that helps you remember your friends birthdays. By .
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Unit Testing Tutorial: Mocking Objects
30 mins
Writing Stubs
Stubs fake a response to method calls of an object. You’ll use stubs to test your code against a web service that isn’t yet finished.
The web team for your project has been tasked with building a website with the same functionality of the app. The user creates an account on the website and can then synchronize the data between the app and the website. But the web team hasn’t even started – and you’re nearly done. Looks like you’ll have to write a stub to stand-in for the web backend.
In this section you will focus on two test methods: one for fetching contacts added to the website, and one to post contacts from your app to the website. In a real-world scenario you’d also need some kind of login mechanism and error handling, but that’s beyond the scope of this tutorial.
Open APICommunicatorProtocol.swift; this protocol declares the two methods for getting contacts from the web service and for posting contacts to the web service.
You could pass around Person
instances, but this would require you to use another managed object context. Using a struct is simpler in this case.
Now open APICommunicator.swift. APICommunicator
conforms to APICommunicatorProtocol
, but right now there’s just enough implementation to keep the compiler happy.
You’ll now create stubs to support the interaction of the view controller with an instance of APICommunicator
.
Open PeopleListViewControllerTests.swift and add the following class definition within the PeopleListViewControllerTests
class:
// 1
class MockAPICommunicator: APICommunicatorProtocol {
var allPersonInfo = [PersonInfo]()
var postPersonGotCalled = false
// 2
func getPeople() -> (NSError?, [PersonInfo]?) {
return (nil, allPersonInfo)
}
// 3
func postPerson(personInfo: PersonInfo) -> NSError? {
postPersonGotCalled = true
return nil
}
}
There are few things to note here:
- Even though
APICommunicator
is a struct, the mock implementation is a class. It’s more convenient to use a class in this case because your tests require you to mutate data. This is a little easier to do in a class than in a struct. -
getPeople()
returns what is stored inallPersonInfo
. Instead of going out on the web and having to download or parse data, you just store contact information in a simple array. -
postPerson(_:)
setspostPersonGotCalled
totrue
.
You’ve just created your “web API” in under 20 lines of code! :]
Now it’s time to test your stub API by ensuring all contacts that come back from the API are added to the persistent store on the device when you call addPerson()
.
Add the following test method to PeopleListViewControllerTests
:
func testFetchingPeopleFromAPICallsAddPeople() {
// given
// 1
let mockDataProvider = MockDataProvider()
viewController.dataProvider = mockDataProvider
// 2
let mockCommunicator = MockAPICommunicator()
mockCommunicator.allPersonInfo = [PersonInfo(firstName: "firstname", lastName: "lastname",
birthday: NSDate())]
viewController.communicator = mockCommunicator
// when
viewController.fetchPeopleFromAPI()
// then
// 3
XCTAssert(mockDataProvider.addPersonGotCalled, "addPerson should have been called")
}
Here’s what going on in the above code:
- First you set up the mock objects
mockDataProvider
andmockCommunicator
you’ll use in the test. - Then you set up some fake contacts and call
fetchPeopleFromAPI()
to make a fake network call. - Finally you test that
addPerson(_:)
was called.
Build and run your tests — all should pass.
Where to Go From Here?
Download the final project here; this version also includes some extra tests that didn’t make it into the tutorial.
You’ve learned how to write mocks, fakes and stubs to test micro features in your app, along with getting a sense how XCTest works in Swift.
The tests in this tutorial are only a starter; I’m sure you already have ideas for tests in your own projects.
For more on unit testing, check out Test Driven Development (TDD) and Behavior Driven Development (BDD). Both are development methodologies (and, frankly, a whole new mindset) where you write the tests before you write the code.
You can listen to tutorial team member Ellen Shapiro discuss unit testing in the official Ray Wenderlich podcast.
Unit Tests are only one part of a complete test suite; integration tests are the next logical step. An easy way to start working with integration tests is UIAutomation. It’s well worth the read if you’re serious about testing your apps — and you should be! :]
If you have any comments or questions about this tutorial, feel free to join the discussion in the forum below!