QuickはRSpec風のBDDスタイル、Nimbleはそれに使うMatcherたち
Quick/Nimbleがそもそもなんぞやという方は↓を一読ください。
 [Xcode/Swift] Quick/NimbleではじめるSwiftの快適ユニットテスト入門
[Xcode/Swift] Quick/NimbleではじめるSwiftの快適ユニットテスト入門
今回はポケモンでざっくり処理の役割を学ぶ回。
Contents 非表示
主要なmatcher
| カテゴリ | Matcher | 意味 | 使用例(概要) | 
|---|---|---|---|
| 値の比較 | equal,beGreaterThan | 値が一致する、大小比較 | expect(level).to(equal(25)) | 
| 存在・空チェック | beNil,beEmpty,contain | nil, 空配列, 含んでいるか | expect(team).to(contain("Pikachu")) | 
| 真偽判定 | beTrue,beFalse | Boolがtrue/falseか | expect(isLegendary).to(beTrue()) | 
| 非同期 | toEventually,toEventuallyNot | 値の変化を待つ | expect(x).toEventually(equal(...)) | 
| 条件式(代替) | to(beTrue()) | 複雑な条件の判定 | expect(x > 10 && y == z).to(beTrue()) | 
サンプルテストコード
import Quick
import Nimble
import Foundation
struct Pokemon: Equatable {
    let name: String
    let level: Int
    let type: String
    let evolution: String?
    let team: [String]
    let isLegendary: Bool
}
final class PokemonSpec: QuickSpec {
    override class func spec() {
        describe("Pokemon Matcher Examples") {
            var pikachu: Pokemon!
            var mewtwo: Pokemon!
            var emptyTeam: [String]!
            beforeEach {
                pikachu = Pokemon(
                    name: "Pikachu",
                    level: 25,
                    type: "Electric",
                    evolution: "Raichu",
                    team: ["Bulbasaur", "Charmander", "Squirtle"],
                    isLegendary: false
                )
                mewtwo = Pokemon(
                    name: "Mewtwo",
                    level: 70,
                    type: "Psychic",
                    evolution: nil,
                    team: [],
                    isLegendary: true
                )
                emptyTeam = []
            }
            // 値の比較
            context("Level Comparison") {
                it("ミュウツーのレベルはピカチュウより高い、ピカチュウのレベルは25") {
                    expect(mewtwo.level).to(beGreaterThan(pikachu.level))
                    expect(pikachu.level).to(equal(25))
                }
            }
            // 存在と空チェック
            context("Team and Evolution Checks") {
                it("nilと空をチェック") {
                    expect(mewtwo.evolution).to(beNil())
                    expect(emptyTeam).to(beEmpty())
                    expect(pikachu.team).to(contain("Bulbasaur"))
                }
            }
            // 真偽チェック
            context("Legendary Status") {
                it("ミュウツーは伝説のポケモンである、ピカチュウはそうではない") {
                    expect(mewtwo.isLegendary).to(beTrue())
                    expect(pikachu.isLegendary).to(beFalse())
                }
            }
            // 非同期チェック (簡易版)
            context("Asynchronous Test With toEventually") {
                it("ピカチュウは進化する") {
                    var evolvingPikachu: Pokemon? = nil
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                        evolvingPikachu = pikachu
                    }
                    expect(evolvingPikachu?.name).toEventually(equal("Pikachu"))
                    expect(evolvingPikachu?.evolution).toEventuallyNot(beNil())
                }
            }
            // 条件チェック & 同一インスタンス
            context("Advanced Matchers") {
                it("特定のConditionを満たすピカチュウ") {
                    expect(pikachu.level >= 20 && pikachu.type == "Electric").to(beTrue())
                }
                it("同一インスタンスのチェック") {
                    let sameMewTwo = mewtwo
                    expect(mewtwo).to(equal(sameMewTwo))
                }
            }
        }
    }
}SwiftLint の nimble_operator 警告がでたら(→ offでもいい or 演算子使ってもOK)
まとめ:テストは「難しさ」より「慣れ」
- Matcherが分かれば、テストコードは「読みやすく、書きやすく」なる
- 今回のような感覚で覚えれば、怖くないし、テスト文化も育つ
- 次のステップ:実アプリで使う、↓が少し参考になるかも
