UITableView.reloadData()を呼んでることのテストの例
テストできない例
ViewControllerの役割:
- repoが値を返した時にtableView.reloadDataを実行する ← このままだとテストできない
ViewController
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を持っているという共通点があることを保証する
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を持っているという共通点があることを保証する
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
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のテスト
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のテスト
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())
}