Post

Fixture vs Stub vs Mock in Swift Testing

Fixtures build test data, stubs control dependency return values, mocks verify interactions — here's the difference with Swift examples.

Fixture vs Stub vs Mock in Swift Testing

All three are test doubles — fake replacements for real dependencies in tests — but they serve distinct purposes.

Quick Mental Model

Each answers a different question:

  • FixtureWhat data do I start with?
  • StubWhat should this dependency return?
  • MockWas this dependency called correctly?

Fixture — Test Data

A fixture is predefined sample data for your models. It is not a replacement for a dependency — it is a convenient way to create realistic objects without filling in every field manually.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Foundation

extension User {
    static func fixture(
        id: UUID = UUID(),
        name: String = "Test User",
        email: String = "test@example.com",
        isPremium: Bool = false
    ) -> User {
        User(id: id, name: name, email: email, isPremium: isPremium)
    }
}

// In your test:
let user = User.fixture(name: "Ali") // all other fields use safe defaults

Use when: You need a model object populated with sensible defaults and the data itself is not the focus of the test.

Stub — Hardcoded Responses

A stub is a fake implementation of a dependency that returns preset values. It replaces something like a network service or database so the test never performs real I/O. A stub cannot fail a test on its own — it only feeds your code the data it needs.

1
2
3
4
5
6
7
8
9
10
11
12
13
import Foundation

// Production protocol
protocol APIServiceProtocol {
    func fetchUser(id: String) async throws -> User
}

// Stub: returns a canned response regardless of input
final class StubAPIService: APIServiceProtocol {
    func fetchUser(id: String) async throws -> User {
        return User.fixture()
    }
}

Use when: Your code under test depends on something (network, database, disk) and you want to control what that dependency returns to test state or output.

Mock — Behavior Verifier

A mock is a stub that also records calls so you can assert on them. You are not only checking what your code returned — you are verifying your code behaved correctly: for example, did it call sendAnalyticsEvent() exactly once?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import Foundation

// Production protocol
protocol AnalyticsProtocol {
    func trackEvent(_ name: String)
}

// Mock: tracks interactions
final class MockAnalyticsService: AnalyticsProtocol {
    private(set) var didCallTrackEvent = false
    private(set) var trackedEventName: String?
    private(set) var callCount = 0

    func trackEvent(_ name: String) {
        didCallTrackEvent = true
        trackedEventName = name
        callCount += 1
    }
}

// In your test:
import XCTest

final class LoginFeatureTests: XCTestCase {
    func testLoginTracksEvent() async throws {
        let mockAnalytics = MockAnalyticsService()
        let sut = LoginViewModel(analytics: mockAnalytics)

        try await sut.login(email: "ali@example.com", password: "secret")

        XCTAssertTrue(mockAnalytics.didCallTrackEvent)
        XCTAssertEqual(mockAnalytics.trackedEventName, "login")
        XCTAssertEqual(mockAnalytics.callCount, 1)
    }
}

Use when: You need to verify that your code interacted with a dependency in a specific way, not just that it returned the right value.

Side-by-Side

 FixtureStubMock
What it isSample data builderFake dependency with preset return valuesFake dependency that records calls
FocusData setupState verificationBehavior verification
Can fail a test?NoNoYes (via assertions on calls)
Asserted on?NoNoYes
Typical useCreating model objectsReplacing network/DBVerifying interactions (analytics, delegates, callbacks)
Swift exampleUser.fixture()StubNetworkServiceMockDelegate with var wasCalled = false

The Rule of Thumb

In Swift unit testing with XCTest or Swift Testing, you will almost always use all three together:

  1. Fixture — build your test data
  2. Stub — control what dependencies return
  3. Mock — assert your code called the right things in the right way

☕ Support My Work

If you found this post helpful and want to support more content like this, you can buy me a coffee!

Your support helps me continue creating useful articles and tips for fellow developers. Thank you! 🙏

This post is licensed under CC BY 4.0 by the author.