LoginSignup
36
34

More than 5 years have passed since last update.

ユニットテストにも可読性を持たせる (Four Phase Test)

Last updated at Posted at 2017-03-07

はじめに

ユニットテストを書いていると、
プロダクトコード同様、可読性が悪くなってきますよね。

ユニットテストの「Four Phase Test」という考え方について、
はじめて知ったので簡単にまとめます。

「Four Phase Test」とは、下記のような4つの手順に分けて、
実施する方法です。

No. フェーズ 説明
1. Setup 前処理
2. Exercise 実行
3. Verify 検証
4. TearDown 後処理

可読性が悪くなる原因

テスト毎の前提条件や、後処理など、
検証以外のテストコードが増えてきたときに、
どこが実行部分で、どこが検証部分か分かりづらくなりがちです。

解決策の方針

そこで、「Four Phase Test」の手順に則って、
テストコードを実装してみる。
(他にもあれば、ご教授願います。)

ユニットテスト全体で共通のSetupとTearDownは、意識していましたが、
ExerciseとVerifyは、意識できていませんでした。

リファクタリング対象のテストコード

コード量も長くなり、可読性が落ちています。

WarikanModelTests.swift
import XCTest
@testable import Warikan

class WarikanModelTests: XCTestCase {

    let usecase = WarikanUsecase()
    let delegete = SpyWarikanDelegate()

    override func setUp() {
        super.setUp()
        usecase.delegate = delegete
    }

    override func tearDown() {
        super.tearDown()
        usecase.delegate = nil
    }

    func test10000div3() {

        let exp = expectation(description: "10000円を3人で割り勘したときのテスト")
        delegete.asyncExpectation = exp

        usecase.calc(totalAmountStr: "10000", numberOfPeopleStr: "3")

        waitForExpectations(timeout: 1) { error in
            if let error = error {
                XCTFail("waitForExpectationsエラー: \(error)")
            }

            switch self.delegete.result! {
            case .success(let bugetStr):
                XCTAssertEqual(bugetStr, "1人辺りの支払い額は、3,400円です。")

            case .error(_) :
                XCTFail("テスト失敗")
            }
        }
    }
}

リファクタリング①

コメントで、4つのフェーズに分けます。
一旦、グルーピングは出来ました。
但し、別の前提条件のテストをするときなど、冗長になることもあります。

WarikanModelTests.swift

/* test10000div3は、割り切れないときのテストを実行します。
 * Check: 10000 / 3 = 3400.
 */
    func test10000div3() {

        // 1. Setup
        let exp = expectation(description: "10000円を3人で割り勘したときのテスト")
        delegete.asyncExpectation = exp

        // 2. Exercise
        usecase.calc(totalAmountStr: "10000", numberOfPeopleStr: "3")

        // 3. Verify
        waitForExpectations(timeout: 1) { error in
            if let error = error {
                XCTFail("waitForExpectationsエラー: \(error)")
            }

            switch self.delegete.result! {
            case .success(let bugetStr):
                XCTAssertEqual(bugetStr, "1人辺りの支払い額は、3,400円です。")

            case .error(_) :
                XCTFail("テスト失敗")
            }
        }

        //4. TearDown
    }

リファクタリング②

ヘルパーメソッドで、4つのフェーズに分けます。
ヘルパーメソッドの切り出すと、
リファクタリング①で上げた冗長な記載はなくなりました。
合わせて可読性も上がりましたね。

WarikanModelTests.swift
/* test10000div3は、割り切れないときのテストを実行します。
 * Check: 10000 / 3 = 3400.
 */
    func test10000div3() {

        // 1. Setup
        whenWarikan(description: "10000円を3人で割り勘したときのテスト")

        // 2. Exercise
        usecase.calc(totalAmountStr: "10000", numberOfPeopleStr: "3")

        // 3. Verify
        verifyWarikanResult(bugetStr: "1人辺りの支払い額は、3,400円です。")

        //4. TearDown
    }

    // 1. Setup
    private func whenWarikan(description: String){
        delegete.asyncExpectation = expectation(description: description)
    }

    // 2. Exercise
    private func exerciseWarikan(totalAmountStr: String, numberOfPeopleStr: String) {
        usecase.calc(totalAmountStr: totalAmountStr, numberOfPeopleStr: numberOfPeopleStr)
    }

    // 3. Verify
    private func verifyWarikanResult(bugetStr: String) {

        waitForExpectations(timeout: 1) { error in
            if let error = error {
                XCTFail("waitForExpectationsエラー: \(error)")
            }

            switch self.delegete.result! {
            case .success(let bugetStr):
                XCTAssertEqual(bugetStr, bugetStr)

            case .error(_) :
                XCTFail("テスト失敗")
            }
        }
    }

まとめ

ユニットテストにも可読性が求められますね。
一つの方針として、「Four Phase Test」も推進していきたいです。

その他の語録

用語  説明
Test テストケース
Suite Testをまとめたもの
SUT 「System Under Test」の略で、テスト対象のこと
Fixture 個別の期待結果を生成するためのまとまり。「Setup」を何らかの形で特化させたものになる。
36
34
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
34