$30 off During Our Annual Pro Sale. View Details »

新規アプリの単体テスト戦略 / unit_tests_strategy_of_new_app

uhooi
October 28, 2022

新規アプリの単体テスト戦略 / unit_tests_strategy_of_new_app

uhooi

October 28, 2022
Tweet

More Decks by uhooi

Other Decks in Programming

Transcript

  1. 新規アプリの

    単体テスト戦略
    2022.10.28(Fri) iOS Test Online
    @the_uhooi

    View Slide

  2. iOS app developer @uhooi
    @uhooi
    @the_uhooi

    View Slide

  3. 入社前の会話
    新規アプリ作って!

    View Slide

  4. 入社前の会話
    新規アプリ作って!
    はい!

    View Slide

  5. ウホーイ入社
    ウホーイ 入社

    View Slide

  6. ・UI: フル SwiftUI

    ・リアクティブ: Combine

    ・非同期処理: Swift Concurrency (async/await)

    ・アーキテクチャ: MVVM

    ・その他: SwiftPM によるマルチモジュール、KMM
    ※開発中なので変更される可能性が大いにあります。
    新規アプリの技術選定

    View Slide

  7. 最初に考えた単体テストの戦略

    View Slide

  8. できる限り にする
    単体テストが不要な設計
    最初に考えた単体テストの戦略

    View Slide

  9. できる限り にする
    単体テストが不要な設計
    必要な にする
    単体テストを書きやすい設計
    最初に考えた単体テストの戦略

    View Slide

  10. 01 単体テストが な

    設計にする
    不要

    View Slide

  11. アーキテクチャ(MVVM)
    View View

    Model UseCase KMM

    View Slide

  12. 単体テストを書く層を明文化する
    View View

    Model UseCase KMM
    : 単体テストを書かない

    : 必要に応じて単体テストを書く

    : 単体テストを書く

    View Slide

  13. View 層の単体テストを書かない理由
    UI/UX を確認するため、

    必ず から
    手動でテストを行う

    View Slide

  14. どうしたら View 層から単体テストをなくせる?
    View View

    Model UseCase KMM
    : 単体テストを書かない

    : 必要に応じて単体テストを書く

    : 単体テストを書く

    View Slide

  15. ロジックを減らせば単体テストも減らせる
    View View

    Model UseCase KMM
    少なくしたい 多くする
    : 単体テストを書かない

    : 必要に応じて単体テストを書く

    : 単体テストを書く
    View 層からロジックを

    なくすのが理想
    ロジック

    View Slide

  16. ロジックを減らすには?
    ロジックを減らすには?

    View Slide

  17. 分岐をなくせばロジックを減らせる
    ロジックを減らすには?
    分岐 をなくす
    (if 文や switch 文)

    View Slide

  18. View に分岐がある(Before)
    // Before

    // View 層

    if viewModel.uiState.isLoading {

    Text("ロード中です。")

    } else {

    Text("ロードが完了しました。")

    }

    View Slide

  19. View から分岐をなくす(After)
    // After

    // View 層

    Text(viewModel.uiState.loadingText)


    // ViewModel 層

    var loadingText: String {

    isLoading ? "ロード中です。" : "ロードが完了しました。"

    }

    View Slide

  20. View から分岐をなくす(After)
    // After

    // View 層

    Text(viewModel.uiState.loadingText)


    // ViewModel 層

    var loadingText: String {

    isLoading ? "ロード中です。" : "ロードが完了しました。"

    }
    View 層の分岐を

    ViewModel 層に寄せる

    View Slide

  21. それでも View から分岐は完全にはなくせない
    // View 層

    struct MonstersView: View {

    var body: some View {

    if viewModel.uiState.monsters.isEmpty {

    EmptyMonsterListView()

    } else {

    MonsterListView(monsters: viewModel.uiState.monsters)

    }

    }

    }

    View Slide

  22. それでも View から分岐は完全にはなくせない
    // View 層

    struct MonstersView: View {

    var body: some View {

    if viewModel.uiState.monsters.isEmpty {

    EmptyMonsterListView()

    } else {

    MonsterListView(monsters: viewModel.uiState.monsters)

    }

    }

    }
    View の出し分けなどの

    分岐はなくせない

    View Slide

  23. それでも View から分岐は完全にはなくせない
    // View 層

    struct MonstersView: View {

    var body: some View {

    if viewModel.uiState.monsters.isEmpty {

    EmptyMonsterListView()

    } else {

    MonsterListView(monsters: viewModel.uiState.monsters)

    }

    }

    }
    View の出し分けなどの

    分岐はなくせない
    monsters が空のときの

    UI の確認が手間

    View Slide

  24. そこでプレビュー
    そこでプレビュー

    View Slide

  25. Xcode 上で SwiftUI の View を確認できる

    View Slide

  26. 「UI を確認するだけの手動テスト」をなくせる
    プレビューでは
    ・端末のサイズ

    ・ライト / ダークモード

    ・データの最小 / 最大値

    ・エラー時の View

     etc...
    様々な状態をかんたんに作り出せる

    View Slide

  27. 02 単体テストを
    設計にする
    書きやすい

    View Slide

  28. ViewModel 層以下の単体テストを書きやすくしたい
    View View

    Model UseCase KMM
    : 単体テストを書かない

    : 必要に応じて単体テストを書く

    : 単体テストを書く

    View Slide

  29. UI フレームワークを View 層のみでインポートする
    View View

    Model UseCase KMM
    SwiftUI Combine
    : 単体テストを書かない

    : 必要に応じて単体テストを書く

    : 単体テストを書く
    View 層のみで UI を完結させる

    View Slide

  30. UI フレームワークを View 層のみでインポートする
    View View

    Model UseCase KMM
    SwiftUI Combine
    : 単体テストを書かない

    : 必要に応じて単体テストを書く

    : 単体テストを書く
    ViewModel 層以下がロジックに集中でき、

    単体テストが書きやすくなる

    View Slide

  31. Task があると単体テストしにくい(Before)
    // Before

    // ViewModel 層

    struct FooViewModel: ObservableObject {

    func didTapLoginButton() {

    Task {

    await login()

    }

    }

    }

    View Slide

  32. Task があると単体テストしにくい(Before)
    // Before

    // ViewModel 層

    struct FooViewModel: ObservableObject {

    func didTapLoginButton() {

    Task {

    await login()

    }

    }

    }
    ログイン処理を待たずに

    テストが終了してしまう

    View Slide

  33. ViewModel 層のメソッドを非同期にする(After)
    // After

    // ViewModel 層

    struct FooViewModel: ObservableObject {

    func didTapLoginButton() async {

    await login()

    }

    }

    View Slide

  34. ViewModel 層のメソッドを非同期にする(After)
    // After

    // ViewModel 層

    struct FooViewModel: ObservableObject {

    func didTapLoginButton() async {

    await login()

    }

    } 非同期にすると

    並行処理もしやすくなる

    View Slide

  35. Task を View 層に書く(After)
    // After

    // View 層

    struct FooView: View {

    // ...

    Task {

    await viewModel.didTapLoginButton()

    }

    // ...

    }

    View Slide

  36. ちなみに…

    View Slide

  37. ちなみに…

    まだ単体テストを書いていません
    これから書きます!

    View Slide

  38. 03 まとめ

    View Slide

  39. まとめ
    設計を工夫することで、



    できる
    不安定な単体テストを減らし
    安定した単体テストを書きやすく

    View Slide

  40. まとめ
    View View

    Model UseCase KMM
    UI ロジック
    Task
    単体テストしにくい 単体テストしやすい

    View Slide

  41. まとめ
    View View

    Model UseCase KMM
    単体テストしにくい・しやすいものを

    分離し、単体テストする層を明確にする
    UI ロジック
    Task
    単体テストしにくい 単体テストしやすい

    View Slide

  42. まとめ
    View View

    Model UseCase KMM
    UI ロジック
    Task
    単体テストを書かない層(View 層)は

    プレビューや手動テストで品質を担保する
    単体テストしにくい 単体テストしやすい

    View Slide