TDDから始める:迅速ユニットテスト


それが分離されたビジネスロジックコードの小さな部分をテストすることになるとき、TDD (Test Driven Development)アプローチは重要です.
このブログでは、どのようにTDDを使用してSwiftuiアプリケーションを行うにはきれいなアーキテクチャを構築する方法をカバーします
“Do to get to case”を実行します.

このタイプのアプリケーションでは、アプリケーションテストの2種類が必要ですUIテストと単体テスト我々はビジネスロジックに集中するので、我々はTDDのユニットテストをカバーするだけでしょう.任意のプロダクションコードを書く前に、まずテストを書きます.
アプリケーションは次のファイルとフォルダ構造で構成されます.
.
├── MyApp
    ├── Data
    │   ├── DataSource
    │   │   ├── TodoDataSource.swift
    │   │   └── DB
    │   │       ├── Entity
    │   │       │    └── TodoDBEntity.swift
    │   │       └── TodoDBDataSourceImpl.swift
    │   └── Repository
    │       └── TodoRepositoryImpl.swift
    ├── Domain
    │    ├── Model
    │    │   └── Todo.swift
    │    ├── Repository
    │    │   └── TodoRepository.swift
    │    └── UseCase
    │        └── GetTodos.swift
    └── Presentation
        ├── TodoViewModel.swift
        └── TodoListView.swift
└── MyAppTests
    ├── Data
    │   ├── DataSource
    │   │   └── DB
    │   │       └── TodoDBDataSourceImplTest.swift
    │   └── Repository
    │       └── TodoRepositoryImplTest.swift
    ├── Domain
    │    └── UseCase
    │        └── GetTodosTest.swift
    └── Presentation
        └── TodoViewModelTest.swift

TDDの実践を通して「ToDoViewModel」を開発したいと思います

TDDに関するノート
TDDは、単位テストを最初に書き、それから元のコードを書くソフトウェア開発プロセスです.TDDでは、コードの設計と開発は単位テストである.
従来の単体テストでは、元のコードが書き込まれた後に単体テストが書き込まれます.これは長期保守性のためだけです.しかし、TDDでは、ユニットテストが最初に書き込まれ、コードを介して進化した.

赤緑色リファクタ
レッドグリーンリファクタは、TDDで興味深い概念です.以下にステージを示す.
赤-最初に失敗した単体テストが作成され、それは赤の状態になります
緑-私たちはちょうどユニットテストパスを作るために関連するコードを変更します
リファクタ-一度テストが通過すると、元の実装が行われるようにコードをリファクタリングできます.
IOS Swiftuiプロジェクトでテストを始めるには、テストターゲットを追加する必要があります.


これでテストテンプレートが作成されます.
テストスイート全体を実行するには、「クラス名」の横にある「実行」ボタンをクリックし、または個々のテストを実行するには、各テストの横にある「ダイヤモンド」ボタンをクリックします.また、各テストの単語“テスト”を開始する必要があることに注意してください

テストとアプリケーションファイルとフォルダ構造を作成しましょう.任意のプロダクションコードなしでファイルを作成します.すべての開発コードは、テストコードで駆動されます.

最初のテストから始めましょう.

MyApptest/プレゼンテーション/ToDoViewModelTest.スウィフト
もちろん、このテストはビルドされず、ファイルが存在しないため失敗します.
私たちは赤い舞台にいます.
テストパスを作るために、最小限のコードを書きましょう.
import Foundation

class TodoViewModel: ObservableObject {

}
テストをもう一度実行すると緑のステージ

また、ビューモデルは依存関係をとっているので、gettodosusecaseを含むようにテストを変更する必要があります

我々は赤いステージに戻っている
ビューモデルを更新しましょう
import Foundation

class TodoViewModel: ObservableObject {

    private var getTodosUseCase : GetTodos

    init(getTodosUseCase: GetTodos){
        self.getTodosUseCase = getTodosUseCase
    }

}
GetToDoSSecseaseのプロトコルと模擬実装を作成する必要があります.私たちがいる間、それはtodoモデルを作りましょう
enum UseCaseError: Error{
    case dataSourceError, decodingError
}

protocol GetTodos {
    func execute() async -> Result<[Todo], UseCaseError>
}
MyApp/domain/usecase/gettodos.スウィフト
import Foundation

struct Todo: Identifiable {
    let id: Int
    let title: String
    let isCompleted: Bool
}
MyApp/ドメイン/モデル/藤堂.スウィフト
Viewモデルをテストして開発しているので、この段階でGetToDosをケースインプリメンテーションを使用する必要はありません
import Foundation
@testable import MyApp

class MockGetTodosUseCase: GetTodos{
    func execute() async -> Result<[Todo], UseCaseError> {
        Result.success([
            Todo(id: 1, title: "Mock Title One", isCompleted: true),
            Todo(id: 2, title: "Mock Title Two", isCompleted: false)
        ])
    }
}
Myapptest/domain/usecase/mockgettodosusecase.スウィフト
テストパス-緑のステージ

ビューモデルの開発を駆動するために2、3のテストを加えましょう

TemptoDoViewModelRankを使用しています.

import Foundation

class TodoViewModel: ObservableObject {

    private var getTodosUseCase : GetTodos

    init(getTodosUseCase: GetTodos){
        self.getTodosUseCase = getTodosUseCase
    }

    @Published
    var todos: [Todo] = []

}


TestToDoViewModelを返します.

import Foundation

class TodoViewModel: ObservableObject {

    private var getTodosUseCase : GetTodos

    init(getTodosUseCase: GetTodos){
        self.getTodosUseCase = getTodosUseCase
    }

    @Published
    var todos: [Todo] = []

    func getTodos() async {

        let result = await self.getTodosUseCase.execute()
        switch result{
        case .success(let todos):
            self.todos = todos
        case .failure(_):
            self.todos = []

        }
    }
}


エラーが発生した場合にTRUEを返します.
エラーになるユースケースを作成しましょう
import Foundation
@testable import MyApp

class MockGetTodosErrorUseCase: GetTodos{
    func execute() async -> Result<[Todo], UseCaseError> {
        Result.failure(.dataSourceError)
    }
}
Myapptest/domain/usecase/mockgettodoserrorusecase.スウィフト

import Foundation

class TodoViewModel: ObservableObject {

    private var getTodosUseCase : GetTodos

    init(getTodosUseCase: GetTodos){
        self.getTodosUseCase = getTodosUseCase
    }

    @Published
    var todos: [Todo] = []


    @Published
    var errorMessage = ""

    func getTodos() async {

        let result = await self.getTodosUseCase.execute()
        switch result{
        case .success(let todos):
            self.todos = todos
        case .failure(let error):
            self.todos = []

            if(error == UseCaseError.dataSourceError){
                errorMessage = "Error"
            }

            if(error == UseCaseError.decodingError){
                errorMessage = "Data Decoding Error"
            }
        }
    }
}

我々のテストは最終的にエラーメッセージが間違っていることを拾っている.「エラー」から「データソースエラー」に変更する必要があります
     if(error == UseCaseError.dataSourceError){
                errorMessage = "Data Source Error"
            }

ToDoビューモデルのコードは、最終的に行われます.他のすべてのビジネスロジックに同じプロセスを使用することができます.