Swift Talking is a blog updated every two weeks about Swift, tooling, Continuous Integration and other stuff written by Alex Salom.

Writing mocks manually in Swift

When writing unit tests we want to check the interactions between the subject under test or sut and its dependencies. In other programming languages like Objective-C we can do this with one of the multiple mocking frameworks available such as OCMock. Unfortunately in Swift we don’t have any framework like these available and the reason for this is that Swift’s reflection capabilities are limited, they provide read-only access to a subset of type metadata. In other words, we can’t modify a class definition in runtime. This leaves us with two options: Write mocks manually or generate them with some short of code generator. Today we’ll talk about the first option.

Writing mocks manually

Protocols are very useful when writing mocks. A protocol defines a blueprint of methods, properties and other requirements that adopting classes, structs or enumerations must implement. A protocol can be adopted by one or several classes.

Let’s say we have the following scenario: A presenter can fetch a user through a repository and update a view when the user is retrieved. We can define the user, the view and the repository as follows:

struct User: Equatable {
  let name: String
  let lastname: String
}

protocol UserView {
  func update(user: User)
}

protocol UserRepository {
  typealias UserCallback = (User) -> Void
  func fetchUser(result: @escaping UserCallback)
}

The actual implementations for the repository and the view are irrelevant for this demonstration. Let’s now create our presenter which will fetch the user’s data and update the view.

class UserPresenter {
  private let view: UserView
  private let repository: UserRepository

  init(view: UserView, repository: UserRepository) {
    self.view = view
    self.repository = repository
  }

  func loadUser() {
    repository.fetchUser { user in
      self.view.update(user: user)
    }
  }
}

So far so good. We can see now how UserPresenter depends and interacts with both the repository and the view. The next step will be testing those interactions. To do this, we’ll go to our test target and create two new classes: UserViewMock and UserRepositoryMock. They will implement the protocols that we defined earlier which will allow us to inject them into UserPresenter.

class UserViewMock: UserView {
  var updateParam: User?
  func update(user: User) {
    updateParam = user
  }
}

class UserRepositoryMock: UserRepository {
  var fetchUserParam: UserCallback?
  var fetchUserWasCalled = false
  func fetchUser(result: @escaping UserCallback) {
    fetchUserParam = result
    fetchUserWasCalled = true
  }
}

We added control variables to the mocks so we can later check from the unit tests that certain functions were called and what kind of parameters were passed to them.

We can now create instances of UserViewMock and UserRepositoryMock, inject them into UserPresenter and validate that the interactions work as expected. We can even fake a response from the fetching function and make it return any data we want.

class UserPresenterTests: XCTestCase {
  var view: UserViewMock!
  var repository: UserRepositoryMock!
  var sut: UserPresenter!

  override func setUp() {
    super.setUp()

    view = UserViewMock()
    repository = UserRepositoryMock()
    sut = UserPresenter(view: view, repository: repository)
  }

  func testLoadUser() {
    sut.loadUser()

    XCTAssertTrue(repository.fetchUserWasCalled)

    let expecedUser = User(name: "Alex", lastname: "Salom")
    repository.fetchUserParam?(expecedUser)

    XCTAssertEqual(view.updateParam, expecedUser)
  }
}

Pretty nice, don’t you think?


But… what about unowned code?

We saw that writing mocks for code that we own is very easy, we only need to make our production and mock classes adopt the same protocol. But what happens with code which we do not own and we can’t modify?

Once again, we need to use protocols. We need to extend the code that we do not own with our own protocols. Let’s see this in action with a simple Counter that relies on UserDefaults.

protocol Defaults {
  func integer(forKey defaultName: String) -> Int
  func set(_ value: Int, forKey defaultName: String)
}

extension UserDefaults: Defaults { }

class Counter {
  private let defaults: Defaults

  init(defaults: Defaults = UserDefaults.standard) {
    self.defaults = defaults
  }

  var count: Int {
    return defaults.integer(forKey: "count")
  }

  func increment() {
    defaults.set(count + 1, forKey: "count")
  }
}

See? We decorated Apple’s UserDefaults with our own protocol named Defaults where we defined functions with the same signature that UserDefaults has. Counter then declares a Defaults instance variable that gets injected through the initializer using a default value of UserDefaults.standard but with type Defaults; which is our own protocol. We can now use the exact same technique as before to implement our own DefaultsMock in the test target.

class DefaultsMock: Defaults {
  var integerParam: String?
  var integerToReturn = 0
  var integerWasCalled = false
  func integer(forKey defaultName: String) -> Int {
    integerWasCalled = true
    integerParam = defaultName
    return integerToReturn
  }

  var setParams: (value: Int, defaultName: String)?
  func set(_ value: Int, forKey defaultName: String) {
    setParams = (value, defaultName)
  }
}

Notice how we used an optional touple in the set function as a control variable where we can group all parameters that are passed into the function. This will reduce the amount of variables we need to define. Instead of defining one control variable per parameter in the funciton’s signature, we define a touple that groups them all.

With all these in place we can write our tests that verify how our Counter implementation interacts with UserDefaults. We’ll write two tests, one will verify how the count is obtained from the UserDefaults and another one that will verify how increasing the counter will first obtain the value from the UserDefaults, increase it by one and then save it again in the UserDefaults.

class CounterTests: XCTestCase {
  var defaults: DefaultsMock!
  var sut: Counter!

  override func setUp() {
    super.setUp()

    defaults = DefaultsMock()
    sut = Counter(defaults: defaults)
  }

  func testRetrieveCount() {
    let retrunedCount = sut.count

    XCTAssertEqual(retrunedCount, defaults.integerToReturn)
    XCTAssertEqual(defaults.integerParam, "count")
  }

  func testIncrementCount() {
    let beforeIncrement = defaults.integerToReturn

    sut.increment()

    XCTAssertTrue(defaults.integerWasCalled)
    XCTAssertEqual(defaults.setParams?.value, beforeIncrement + 1)
    XCTAssertEqual(defaults.setParams?.defaultName, "count")
  }
}

This is how you can mock code which you don’t have access and can’t modify.


Alex Salom

Hi! My name is Alex Salom and I’m an iOS Engineer. In this site I’ll share with you tricks & tips related to iOS and also to everything that happens around it: tooling, Continuous Integration and Continuous Delivery. You can find me as well in Twitter, GitHub and LinkedIn.