Let's consider a TaskManager class that has NetworkService as a collaborator; the tasks are stored locally until the user decides to save them on a server by tapping on a synchronization button.
Basically, we have two different behaviours in the class, and we want to test them in different scenarios.
However, when we test one of these behaviors, we need to take the other into account somehow.
The code could be something similar to this:
class TaskManager {
private var tasks = [Task]()
private let service: TaskManagerService
init(service: TaskManagerService) {
self.service = service
}
func add(task: Task) {
tasks.append(task)
}
var count: Int {
return tasks.count
}
func sync() {
service.sync(tasks: [Task])
}
}
protocol TaskManagerService {
func fetchAllTasks(completion: ([Task]) -> Void)
func sync(tasks: [Task])
// ...
}
struct Task {
// ---
}
When we want to test, for example, that the count of the tasks is equal to the task we add, we don't need to consider the network service, but it's needed to compile the class.
Remembering what we saw in Chapter 11, Implementing Dependency Injection about Dependency Injection, we defined the collaboration using a protocol, so that we could replace it with a dummy service:
struct TaskManagerServiceDummy: TaskManagerService {
func fetchAllTasks(completion: ([Task]) -> Void) {}
func sync(tasks: [Task]) {}
}
The test can be easily written like this:
func testNumberOfTasks() {
let taskManager = TaskManager(service: TaskManagerServiceDummy())
taskManager.add(task: Task())
taskManager.add(task: Task())
XCTAssertEqual(taskManager.count, 2)
}
A dummy object is a placeholder passed to the SUT that is never called during the test.