0Pricing
Swift Academy · レッスン

ViewModelのテスト

プレゼンテーションロジックを単体でテストします。

「ViewModelのテスト」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。

ViewModelをテストする理由

ViewModelはプレゼンテーションロジックを保持するため、単体テストを行う価値が最も高い層です。UIKitやSwiftUIに依存しないため、単独で高速かつ決定論的にテストできます。

テストしやすい設計

適切に構築されたViewModelは、初期化子を通して依存関係を受け取ります。これにより、テストでフェイクに置き換えられます。

final class WeatherViewModel: ObservableObject {
    @Published var temperatureText = ""
    private let service: WeatherService

    init(service: WeatherService) {
        self.service = service
    }
}

依存性の注入

具体的な依存関係をプロトコルの背後に隠します。ViewModelは抽象化に依存するため、テストではモックを注入できます。

protocol WeatherService {
    func fetchTemperature() async throws -> Double
}

モックを作成する

モックはプロトコルに準拠し、あらかじめ用意したデータを返します。ViewModelが受け取る内容を正確に制御できます。

final class MockWeatherService: WeatherService {
    var stubbedTemperature: Double = 20
    var didCallFetch = false

    func fetchTemperature() async throws -> Double {
        didCallFetch = true
        return stubbedTemperature
    }
}

基本的な単体テスト

モックを注入したら、ViewModelがデータを正しく変換することをアサートします。

func testFormatsTemperature() async {
    let mock = MockWeatherService()
    mock.stubbedTemperature = 25
    let vm = WeatherViewModel(service: mock)

    await vm.load()

    XCTAssertEqual(vm.temperatureText, "25 C")
}

状態遷移をテストする

ViewModelは状態を表すenumを公開することがよくあります。その状態を生成する入力を与えて、各分岐をテストします。

func testLoadingThenLoaded() async {
    let vm = FeedViewModel(service: MockFeedService())
    XCTAssertEqual(vm.state, .loading)

    await vm.load()

    XCTAssertEqual(vm.state, .loaded(["A", "B"]))
}

エラー経路をテストする

モックでエラーをスローし、ViewModelが失敗を適切に処理してエラー状態を公開することを確認します。

final class FailingService: WeatherService {
    func fetchTemperature() async throws -> Double {
        throw URLError(.notConnectedToInternet)
    }
}

func testShowsErrorOnFailure() async {
    let vm = WeatherViewModel(service: FailingService())
    await vm.load()
    XCTAssertEqual(vm.state, .error)
}

インタラクションを検証する

結果だけでなく、依存関係が呼び出されたことを確認したい場合もあります。モックにスパイ用のフラグを持たせると、動作をアサートできます。

func testFetchIsCalled() async {
    let mock = MockWeatherService()
    let vm = WeatherViewModel(service: mock)
    await vm.load()
    XCTAssertTrue(mock.didCallFetch)
}

@Publishedの出力をテストする

Combineを使って@Publishedプロパティを購読し、発行された値を取得して、その順序をアサートできます。

var received: [String] = []
let cancellable = vm.$temperatureText.sink { received.append($0) }

await vm.load()

XCTAssertEqual(received.last, "25 C")
cancellable.cancel()

テストを高速に保つ

モックは即座に値を返し、実際のネットワークも使用しないため、ViewModelのテストは数ミリ秒で実行できます。実際のタイマーやURLSessionは避け、クロックやスケジューラにもフェイクを注入します。

ベストプラクティス

効果的なViewModelテストのポイントは次のとおりです。

  • すべての依存関係をプロトコル経由で注入する
  • 1つのテストメソッドで1つの動作をテストする
  • 成功、空、エラーの経路を網羅する
  • ビューが読み取る公開出力をアサートする

理解度チェック

ViewModelのテストについての知識を確認しましょう。

まとめ

ViewModelを単独で単体テストする方法を学びました。

  • プロトコルの背後にある依存関係を注入する
  • モックで入力を制御し、呼び出しをスパイする
  • 成功、エラー、空の状態を網羅する
  • @Publishedの出力と状態遷移をアサートする

テストしやすいViewModelは、MVVMの関心の分離によって得られる成果です。

よくある質問

「ViewModelのテスト」レッスンは無料ですか?

はい。「ViewModelのテスト」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。

「ViewModelのテスト」で何を学びますか?

プレゼンテーションロジックを単体でテストします。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「ViewModelのテスト」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. MVVMパターン
  2. ViewModelとViewのバインディング
  3. Coordinatorパターン
  4. ViewModelのテスト
← Swift Academyに戻る