0Pricing
Swift Academy · Lesson

Protocol-Based Mocking and Dependency Injection

Injecting test doubles via protocols without third-party mocking frameworks.

Protocol-Based Mocking and Dependency Injection is a free Swift Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Protocol-Based Mocking?

Using protocols as seams enables swapping real implementations with test doubles without modifying production code.

protocol NetworkService {
  func fetch(url: URL) async throws -> Data
}
class RealNetworkService: NetworkService { /* URLSession */ }
class MockNetworkService: NetworkService { /* returns fixtures */ }

Defining the Protocol

Extract a protocol from your dependency, containing only the methods your class needs.

protocol UserRepository {
  func getUser(id: Int) async throws -> User
  func saveUser(_ user: User) async throws
}

Injecting via Initializer

Pass the dependency through the initializer so tests can supply a mock.

class ProfileViewModel {
  private let repo: UserRepository
  init(repo: UserRepository = RealUserRepository()) {
    self.repo = repo
  }
}

Writing a Mock

Implement the protocol with controllable state: record calls and return stub values.

class MockUserRepo: UserRepository {
  var fetchedIds: [Int] = []
  var stubbedUser: User = User(id: 1, name: "Test")
  func getUser(id: Int) async throws -> User {
    fetchedIds.append(id)
    return stubbedUser
  }
  func saveUser(_ user: User) async throws {}
}

Stub Return Values

Configure stubs before the test to control what the mock returns, simulating different scenarios.

let mock = MockUserRepo()
mock.stubbedUser = User(id: 42, name: "Alice")
let vm = ProfileViewModel(repo: mock)
await vm.loadProfile(id: 42)
XCTAssertEqual(vm.userName, "Alice")

Verifying Interactions

After exercising the system under test, assert that the mock was called with the expected arguments.

XCTAssertEqual(mock.fetchedIds, [42], "Should fetch user 42 exactly once")

Error Simulation

Throw errors from the mock to test error-handling paths in the production code.

class FailingRepo: UserRepository {
  func getUser(id: Int) async throws -> User { throw NetworkError.notFound }
  func saveUser(_ user: User) async throws { throw NetworkError.serverError(500) }
}

Property Injection

Alternatively, expose the dependency as a settable property for simpler replacement in tests.

class OrderService {
  var paymentGateway: PaymentGateway = StripeGateway()
}
let service = OrderService()
service.paymentGateway = MockPaymentGateway()

@Environment / @EnvironmentObject Injection

SwiftUI apps inject dependencies through the environment, making views testable by supplying mock environment objects.

let mockStore = MockCartStore()
let view = CartView().environmentObject(mockStore)

Avoiding Over-Mocking

Mock only direct dependencies; use real collaborators when they are deterministic and fast.

// OK to use real DateFormatter in tests — it's deterministic
// Mock URLSession — network I/O is slow and non-deterministic

Protocol Composition for Narrower Interfaces

Use smaller, focused protocols (Interface Segregation Principle) so mocks only implement what's needed.

protocol Readable { func read() async throws -> [Item] }
protocol Writable { func write(_ item: Item) async throws }
typealias ReadWrite = Readable & Writable

Quick Check

What is the primary benefit of injecting dependencies via a protocol rather than a concrete type?

Lesson Recap

Define a protocol seam for each dependency. Inject via initializer. Implement mocks that record calls and return stubs. Simulate errors with throwing mocks. Verify interactions with XCTAssertEqual on recorded call arrays.

Frequently asked questions

Is the “Protocol-Based Mocking and Dependency Injection” lesson free?

Yes — the full text of “Protocol-Based Mocking and Dependency Injection” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.

What will I learn in “Protocol-Based Mocking and Dependency Injection”?

Injecting test doubles via protocols without third-party mocking frameworks. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Swift Academy?

No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Protocol-Based Mocking and Dependency Injection” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Swift Academy lesson?

Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. XCTestCase Setup, Teardown and Test Methods
  2. XCTAssert Family and Throwing Assertions
  3. Protocol-Based Mocking and Dependency Injection
  4. Async Tests and Performance Measurement
← Back to Swift Academy