To mock or not to mock

I’ve written a book to teach you how to write unit tests effectively. If you want to make your tests fast so you’ll actually run them and run them often to receive immediate feedback, check out Effective Unit Testing. You’ll develop the confidence that your change will not break existing features, and never have to worry about introducing regression. Write non-fragile unit tests that are assets, not liabilities. Use TDD to write testable code that drives feature development.

You can’t talk about unit testing and TDD without mentioning mocks. But what are mocks, really?

In talking to other iOS developers, I frequently see comments such as:

I used to go crazy and write test for every IBOutlet. I would mock every UILabel to verify that they get the correct text based on my models. But it was very time consuming. It was such an overkill. Now I only test if I have complicated logic.

This post serves as a primer to mocking in iOS. It attempts to answer the five Ws and two Hs.

  • Who should mock?
  • Why do you want to mock?
  • When do you want to mock?
  • What do you mock?
  • Where do you put your mocks?
  • How do you mock in a Swift iOS project?
  • How much do you mock?

Who should mock?

Unit tests in test driven development are written by the developers. Mocks are written to help unit testing. This means the developers need to understand mocking in order to effectively test the code they write.

Why do you want to mock?

The iOS apps you develop today are no longer trivial. They aren’t just a few buttons and labels put together in a nice UI anymore. They involve database, networking, mapping, in-app purchase, image, audio and video. A typical iOS app consists of many different components working together. Mocking is a technique to isolate dependencies so you can test one component at a time.

You write mocks to isolate other components so you can test the behavior of the subject under test. Mocking also allows you to introspect your mock objects to verify its observed behavior match your expected behavior.

When do you want to mock?

In TDD, you write your tests first to drive the design of you code. Over time, you’ll inevitably run into situations where you know one component work and you are developing another component. And these components interact with each other. This is the time you mock the working component while developing the new component.

What do you mock?

Testable code is only possible with a good architecture. And a good architecture provides clear boundaries in a complex system. You can manage the dependencies across the boundaries so you can isolate and test each component on its own, one by one. This separation of concerns is the essence of the Single Responsibility Principle (SRP).

You establish an interface, or API, for each component in your iOS app using protocols. And you mock out a component by creating a new class that conform to the protocols. The mock provides a simple, partial, minimum implementation. Its goal is to do just enough so you can test another component.

To quote Uncle Bob:

The mock is not so interested in the return values of functions. It’s more interested in what function were called, with what arguments, when, and how often.

Where do you put your mocks?

The best place to put your mocks is inside your XCTestCase subclass. A different test case can have its own mocks. The benefit is each test case is independently sufficient and doesn’t depend on any other test case.

How do you mock in a Swift iOS project?

Assume you have a displayUser() method in your view controller that set some labels and image views after a user is fetched.

Let’s look at the mocking approach and then the no mock approach.

The mocking approach

You can mock each of the UILabel, and verify its text property to equal the expected name and phone number in the userViewModel. You can also mock the UIImageView and verify its image property is set to the profilePhoto in the userViewModel.

Here is how you can mock the UILabel and UIImageView:

You then use the mocks in your test like:

In the Given, you use setter dependency injection to use your new mocks for the labels and image view. In the Then, you assert that the text and image setters in your mocks are invoked AND they equal to your expected values.

To take it even farther, your IBOutlets are technically private. So you want to use constructor dependency injection instead. Now you need to write a custom initializer for your view controller to take any UILabel and UIImageView subclasses for its IBOutlets. You can take it as far as you want. But I ain’t gonna show the code for that.

The no mock approach

In the Clean Swift architecture, the presenter component provides the input to the view controller by invoking the displayUser() method with the userViewModel argument. The view controller then asks its output – the UI – to display the user info. Specifically, the view controller asks the UI to:

  • Display the user name in the user label by invoking the nameLabel’s text=() setter method.
  • Display the user phone number in the phone label by invoking the phoneLabel’s text=() setter method.
  • Display the user profile photo in the profile photo image view by invoking the profilePhotoImageView’s image=() setter method.

Although it doesn’t exist in code, you can imagine there is a virtual view controller-to-UI boundary that has the following interface:

So in your test for the displayUser() method, you can simply do the following:

No mock. Less setup. Simpler asserts. I like.

You can find the source code for this post on GitHub.

How much do you mock?

The following sums up Uncle Bob’s post on When to Mock:

No mocks
– Slow test suite – database, API, … are slow and asynchronous
– Low test coverage – can’t test all states and edge cases
– Fragile tests – slow, down, changing dependencies

Too many mocks
– Slow mocks – mocks that depend on reflection can be slow
– Fragile tests – mocks that return other mocks are complicated and tightly coupled
– Too many protocols – a mock provides a simpler implementation of a protocol interface

It’s about finding the right balance.

You should mock across architecturally significant boundaries, but not within those boundaries.

In the Clean Swift architecture, you shouldn’t mock UILabel. But you should:
– Mock the interactor when you test the view controller.
– Mock the presenter when you test the interactor.
– Mock the view controller when you test the presenter.
– Mock the database, API, …

When you do mock, write your own mocks:
– They are very easy to write (even easier than I just showed you)
– Simpler mocks mean faster mocks
– No need for mocking framework
– Fewer dependencies to learn

So it’s up to you whether to mock or not to mock!

Raymond
I've been developing in iOS since the iPhone debuted, jumped on Swift when it was announced. Writing well-tested apps with a clean architecture has been my goal.

4 Comments

  1. Hi, Raymond!
    I just start to learn swift, and do learn a lot from all your posts!!!
    But I have a very basic simple question after reading this post.
    You mentioned that the objects transferred between the VIP boundary should contain only primitive types, but in the example code above, the ViewModel seems to have an UIImage, is that comply with the primitive type policy?
    I ask this question because I’m trying to develop an iOS application that needs to render a UI programatically.
    Initially I was thinking about whether it was possible to let a presenter make the UI control and pass back to the viewcontroller, but after a second thought, I gave up this approach after considering the “prmitive type policy”. But after reading this article, I think maybe it’s possible to do it this way.
    I know VIP is a great tool to help developer write clean swift code, not a strict law to follow, but it would be very kind of you to give me some suggestion.

    Thanks!!!

    1. Hi Travis,

      As you already mentioned, Clean Swift is a set of guidelines, not strict rules, to follow. You should try to have only primitive types in your view models. The main reason is that your view controller should know only to display what it’s being told, whether it’s an order, a person, and whatnot. The interactor only needs to know about these orders and persons, not the labels and textfields. So the presenter acts as a intermediary to provide this isolation. If you have a bug in the future, it is super easy to know where to look.

      You can use UIImage or NSURL to represent your image in the view models. There’s not one right answer. It just depends on your situation and what you think as primitive.

Leave a Comment

Your email address will not be published. Required fields are marked *