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 .

2.9 (8) · 1 Review

Save for later
Share

Screen Shot 2015-05-07 at 3.10.07 PM

Who needs unit tests? Not you — your code is perfect. Umm…so you’re just reading this tutorial for your “friend” who needs to learn more about writing unit tests in Swift, right? Right. :]

Unit tests are a great way to write better code; tests help you find most of the bugs early on in the process, but more importantly, writing code in a test-based development mindset helps you write modular code that’s easy to maintain. As a rule of thumb: if your code isn’t easy to test, it’s not going to be easy to maintain or debug.

Unit tests deal with isolated “micro features”. Often you need to mock classes — that is, provide fake yet functional implementations — to isolate a specific micro feature so it can be tested. In Objective-C there are several third-party frameworks that help with mocking and stubbing. But those rely on introspection, which isn’t yet available on Swift. Someday, hopefully! :]

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.

Getting Started

Download the starter project here; this is a basic contacts app that can be hooked up to a web backend. You won’t work on the core app functionality; rather, you’ll write some tests for it to make sure it behaves as expected.

Build and run your app to see how it works. Tap the plus sign and add good ol’ John Appleseed to your list:

iOS Simulator Screen Shot 31.03.2015 21.55.29

The sample app uses Core Data to store your contacts.

ragecomic_coredata

Don’t panic! :] You don’t need any experience with Core Data for this tutorial; there’s no rocket science involved.

Note: If you do want to become a Core Data master, you can get started by reading this Core Data: Getting Started tutorial.

Note: If you do want to become a Core Data master, you can get started by reading this Core Data: Getting Started tutorial.

Advantages and Disadvantages of Unit Tests

When it comes to testing, there’s good news, and bad news. The bad news is that there can be disadvantages to unit tests, like the following:

  • More code: In projects with high test coverage it’s possible to have more test code than functional code.
  • More to maintain: When there is more code, there is more to maintain.
  • No silver bullet: Unit tests don’t (and can’t) ensure that your code is free of bugs.
  • Takes longer: Writing tests takes time — time you could spend learning new exciting stuff on raywenderlich.com!

Although there is no silver bullet, there is a silver lining — testing has the following advantages:

  • Confidence: You can demonstrate that your code works.
  • Quick feedback: You can use unit tests to quickly validate code that is buried many layers deep in your app navigation — things that are cumbersome to test manually.
  • Modularity: Unit tests help keep you focused on writing more modular code.
  • Focus: Writing tests for micro features keep you focused on the small details.
  • Regression: Be sure that the bugs you fixed stay fixed — and aren’t broken by subsequent fixes.
  • Refactoring: Until Xcode gets smart enough to refactor your code on its own, you’ll need unit tests to validate your refactoring.
  • Documentation: Unit tests describe what you think the code should do; they serve as another way to document your code.

The Basic App Structure

A lot of the code in the sample app is based on the Master-Detail Application template with Core Data enabled. But there are some significant improvements over the template code. Open the sample project in Xcode and have a look at the project navigator:

Screen Shot 2015-03-28 at 16.59.36

Take note of the following details:

  • There is a Person.swift and a PersonInfo.swift file. The Person class is an NSManagedObject that contains some basic information about each person. The PersonInfo struct contains the same information but can be instanced from the address book.
  • The folder PeopleList has three files: A view controller, a data provider and a data provider protocol.

The file collection in PeopleList is an attempt to avoid massive view controllers. It’s good practice to avoid massive view controllers by moving some responsibilities into other classes that communicate with the view controllers via a simple protocol. You can learn more about massive view controllers and how to avoid them by reading this interesting albeit older article.

In this case, the protocol is defined in PeopleListDataProviderProtocol.swift; open it and have a look. A class conforming to this protocol must have the properties managedObjectContext and tableView and must define the methods addPerson(_:) and fetch(). In addition, it must conform to the UITableViewDataSource protocol.

The view controller PeopleListViewController has a property dataProvider, which conforms to PeopleListDataProviderProtocol. This property is set to an instance of PeopleListDataProvider in AppDelegate.swift.

You add people to the list using ABPeoplePickerNavigationController. This class lets you, the developer, access the user’s contacts without requiring explicit permission.

PeopleListDataProvider is responsible for filling the table view and for talking to the Core Data persistent store.

Note: Several classes and methods in the starter project are declared as public; this is so the test target can access those classes and methods. The test target is outside of the app module. If you don’t add any access modifier the classes and methods are defined as internal. This means they are only accessible within the same module. To access them from outside the module (for example from the test target) you need to add the public access modifier.

Note: Several classes and methods in the starter project are declared as public; this is so the test target can access those classes and methods. The test target is outside of the app module. If you don’t add any access modifier the classes and methods are defined as internal. This means they are only accessible within the same module. To access them from outside the module (for example from the test target) you need to add the public access modifier.

That’s enough overview — time to start writing some tests!