Browse Questions
Answer:
Inject dependencies through init using protocols. This lets tests swap real implementations with mocks, making tests fast and deterministic.
Code Example:
// Define behaviour as protocols
protocol Clock {
var now: Date { get }
}
protocol Storage {
func save(_ data: Data, key: String) throws
func load(key: String) throws -> Data
}
// Real implementations
struct SystemClock: Clock { var now: Date { Date() } }
struct UserDefaultsStorage: Storage { ... }
// ViewModel takes protocols
class SessionViewModel {
private let clock: Clock
private let storage: Storage
init(clock: Clock = SystemClock(),
storage: Storage = UserDefaultsStorage()) {
self.clock = clock
self.storage = storage
}
func saveSession() throws {
let session = Session(createdAt: clock.now)
try storage.save(try JSONEncoder().encode(session), key: "session")
}
}
// In tests — control time and storage
class MockClock: Clock { var now = Date(timeIntervalSince1970: 0) }
class MockStorage: Storage {
var store: [String: Data] = [:]
func save(_ data: Data, key: String) { store[key] = data }
func load(key: String) throws -> Data { store[key]! }
}