Mocks are entities used to verify expectations. As dummies, they don’t implement any functionality, just the bare minimum to capture its interaction with the subject under test.

Using the same protocol from the Dummy example, ShapeProtocol,

protocol ShapeProtocol {
    func draw(at point: CGPoint, radius: Float)
}

a basic example of a mock would be:

class ShapeMock: ShapeProtocol {
    private(set) var didCallDraw = false
    private(set) var lastPoint: CGPoint?
    private(set) var lastRadius: Float?

    func draw(at point: CGPoint, radius: Float) {
        didCallDraw = true
        lastPoint = point
        lastRadius = radius
    }
}

The method draw(at:radius:) captures all the parameters passed to it, and sets a boolean indicating if the method was called or not.

Suppose we want to test a type, ShapeDrawer, which has a property shape:

struct ShapeDrawer {
    let shape: ShapeProtocol

    func draw() {
        shape.draw(at: .zero, radius: 5.0)
    }
}

In order to test it, instead of passing a real object , we pass an instance of ShapeMock, a concrete implementation of ShapeProtocol. This allows us to focus on the subject under test, ShapeDrawer, while controlling the external entity.

func testDraw() {
    // Given

    let shapeMock = ShapeMock()

    let drawer = ShapeDrawer(
        shape: shapeMock
    )

    // When

    drawer.draw()

    // Then

    XCTAssertTrue(
        shapeMock.didCallDraw,
        "It should call draw"
    )

    XCTAssertEqual(
        shapeMock.lastPoint, .zero,
        "It should pass the correct point"
    )

    XCTAssertEqual(
        shapeMock.lastRadius, 5.0,
        "It should pass the correct radius"
    )
}

There are tools to automatically generate mocks from protocols, such as [Sourcery], but the idea is exactly the same: capturing interaction to verify expectations. Sourcery is my favorite solution to deal with mocks, it saves time with all the boilerplate of writing mocks, while creating a consistent interface for all the mocks.

Some engineers prefer to avoid protocols, working with simple types such as structs and enums. In this scenario, ShapeProtocol would be replaced by a real type :

struct Shape {
    var draw: (CGPoint, Float) -> Void
}

while ShapeDrawer gets a real object injected:

struct ShapeDrawer {
    let shape: Shape

    func draw() {
        shape.draw(.zero, 5.0)
    }
}

The mock, in this scenario is a simple instance of Shape, using its closure to capture parameters:

func testDraw() {
    var passedPoint: CGPoint?
    var passedRadius: Float?

    // Given

    let drawer = ShapeDrawer(
        shape: .init(
            draw: { point, radius in
                passedPoint = point
                passedRadius = radius
            }
        )
    )

    // When

    drawer.draw()

    // Then

    XCTAssertEqual(
        passedPoint, .zero,
        "It should pass the correct point"
    )

    XCTAssertEqual(
        passedRadius, 5.0,
        "It should pas the correct radius"
    )
}

Where to go from here:

  1. Introduction
  2. The Different Types of Tests
  3. Test Doubles
    1. Dummies
    2. Mocks
    3. Stubs
    4. Partials
    5. Fakes
  4. Fixtures
  5. Frameworks and Tools