テストダブル：Spy編

UITableView.reloadData()を呼んでることのテストの例

## テストできない例

**ViewControllerの役割：**

- repoが値を返した時にtableView.reloadDataを実行する ← このままだとテストできない

**ViewController**

```swift
class ToDoListViewController: UIViewController {
    private var toDoItems: [ToDoItem] = []
    private var toDoListRepo: ToDoListRepository

	init(toDoListRepo: ToDoListRepository) {
		self.toDoListRepo = toDoListRepo
	}

    override func viewDidLoad() {
        super.viewDidLoad()
        toDoListRepo
            .getAll()
			.onSuccess { toDoItems in
                self.toDoItems = toDoItems
                self.tableView.reloadData() //<- これを呼んでることのテストができない
            }
    }
}
```

## テストできる例

ViewControllerの役割をちょっと変える。あと新しい登場人物を追加する

**Reloaderの役割：**

- reloadメソッドの引数として、”reloadDataメソッドを持っている何か”（Reloadableに準拠したクラス）を受け取る
- 受け取った”reloadDataメソッドを持ってる何か”のreloadDataメソッドを実行する

**ViewControllerの役割：**

- “reloadメソッドを持ってる何か”（Reloaderに準拠したクラス）のreloadを実行する
- reloadDataの引数にtableViewを渡す

**Reloadableたち**
SpyReloadableもUITableViewも、reloadDataを持っているという共通点があることを保証する

```swift
protocol Reloadable {
    func reloadData()
}

//Reloadableに準拠させる
class SpyReloadable: Reloadable {
	//reloadDataが呼ばれたらtrueになる、呼ばれなかったらfalseのまま
    private(set) var reloadData_wasCalled: Bool = false
    func reloadData() {
        reloadData_wasCalled = true
    }
}

//Reloadableに準拠させる
//UITableViewはreloadDataメソッドを持っているので準拠させれる
extension UITableView: Reloadable {}
```

**Reloaderたち**
SpyReloaderもDefaultReloaderも、reloadを持っているという共通点があることを保証する

```swift
protocol Reloader {
    func reload(reloadable: Reloadable)
}

//Reloaderに準拠させる
class SpyReloader: Reloader {
    //reload実行時の引数（reloadable）に何を渡されたか覚えておく
    private(set) var reload_argument_reloadable: Reloadable? = nil

	//reloadDataメソッドを持ってればなんでもいい
    func reload(reloadable: Reloadable) {　
        reload_argument_reloadable = reloadable
    }
}

//Reloaderに準拠させる
class DefaultReloader: Reloader {
	//reloadDataメソッドを持ってればなんでもいい
    func reload(reloadable: Reloadable) {　
		//reloadDataメソッド実行する
        reloadable.reloadData()
    }
}
```

**ViewController**

```swift
class ToDoListViewController: UIViewController {
    private var toDoItems: [ToDoItem] = []
    private var toDoListRepo: ToDoListRepository
    private var reloader: Reloader //reloadメソッドを持ってればなんでもいい

		init(toDoListRepo: ToDoListRepository, reloader: Reloader) {
			self.toDoListRepo = toDoListRepo
	    	self.reloader = reloader
		}

    override func viewDidLoad() {
        super.viewDidLoad()
        toDoListRepo
            .getAll()
				.onSuccess { toDoItems in
                self.toDoItems = toDoItems

				//”reloadメソッドを持っている何か”のreloadメソッドに、
				//reloadDataメソッドを持っているtableViewを渡して実行する
                self.reloader.reload(reloadable: self.tableView)
            }
    }
}
```

## テストコード（Quick、Nimble使用）

**ViewControllerのテスト**

```swift
it("reloads the tableview's data once the repository returns data") {
	stubToDoListRepository = StubToDoListRepository()
    stubToDoListRepository.getAll_returnPromise.success([])
	spyReloader = SpyReloader()

    toDoListTableViewController = ToDoListViewController(
		toDoListRepo: stubToDoListRepository,
        reloader: spyReloader  //実行時に引数として何を渡されたか覚えといてくれるやつを渡す
    )
    toDoListTableViewController.loadViewIfNeeded()
    
    
    //なにを渡されて実行されたかチェックする
    expect(spyReloader.reload_argument_reloadable).toEventually(beAKindOf(UITableView.self))
}
```

**Reloaderのテスト**

```swift
it("calls reload on the object that is passed in") {
	let defaultReloader = DefaultReloader()
    let spyReloadable = SpyReloadable()

    //実行されたか、されてないか覚えといてくれるやつを渡して実行する
    defaultReloader.reload(reloadable: spyReloadable)
    
    //渡したものが実行されてるか確認する
    expect(spyReloadable.reloadData_wasCalled).to(beTrue())
}
```