iOSSnapshotTestCaseを使用してスナップショットテストを行う際の大まかな実装についての個人的なまとめ
iOSSnapshotTestCase: https://github.com/uber/ios-snapshot-test-case
前置き
スナップショットテストはViewのテストであり、ロジックのテストでは無い
ロジックはロジックでテストを実装すること
スナップショットテストの目的
Viewのリグレッションテストを全て人間がやるのは辛いので、ある程度の自動化を図る
実装
言語
とデバイス
の組み合わせ毎のスナップショットテストを行うサンプル
テストクラスの外で実装したfunctionの挙動についてはソースコメント参照
import FBSnapshotTestCase
@testable import SnapShotTestExample
class FBSnapshotTestCaseSwiftTest: TestCase {
private let baseball = Sample.baseball
|> Sample.lens.state .~ .successful // テスト用のtemplateデータをテストケースに合わせて再作成する(state = .successful)
private let yamada = User.yamada
private let xxx = Xxx.template
override func setUp() {
super.setUp()
UIView.setAnimationsEnabled(false)
// recordMode = true
}
override func tearDown() {
UIView.setAnimationsEnabled(true)
super.tearDown()
}
func testXxx() {
// Mockを用意する
let service = MockService(fetchXxxResponce: self.xxx)
// LanguageとDeviceの組み合わせのタプルを回す
combos(Language.allCases, [Device.phone4_7inch, Device.phone5_8inch, Device.pad]).forEach {
language, device in
withEnvironment( // テスト用の一時的な環境を作ってテストを実行する
apiService: service,
language: language
) {
let controller = SampleViewController.configuredWith(sample: self.baseball, user: self.yamada)
// 指定した `デバイス(サイズ)` と `向き` のviewcontrollerを作る
let (parent, _) = traitControllers(device: device, orientation: .portrait, child: controller)
// スナップショットテスト
FBSnapshotVerifyView(parent.view, identifier: "lang_\(language)_device_\(device)")
}
}
}
func testXxx_Canceled() {
// テスト用のtemplateデータをテストケースに合わせて再作成する(status = .canceled)
let xxxCanceled = .template |> Xxx.lens.status .~ .canceled
let service = MockService(fetchXxxResponce: xxxCanceled)
combos(Language.allCases, [Device.phone4_7inch, Device.phone5_8inch, Device.pad]).forEach {
language, device in
withEnvironment(
apiService: service,
language: language
) {
let controller = SampleViewController.configuredWith(sample: self.baseball, user: self.yamada)
let (parent, _) = traitControllers(device: device, orientation: .portrait, child: controller)
FBSnapshotVerifyView(parent.view, identifier: "lang_\(language)_device_\(device)")
}
}
}
}
extension Sample {
internal static let template = Sample(
id: 1,
name: "Sample",
state: .live
)
// 代表的なテストデータtemplateを定義する(id = 50, name = "Baseball")
internal static let baseball = .template
|> Sample.lens.id .~ 50
|> Sample.lens.name .~ "Baseball"
}
最後に
makefileなどを使ってコマンドでテストを実行できるようにすると良い