XCTestの実行順序を確認する

あまり意識したことなかったのですが、XCTestがどのような順序で実行されるかについて、WWDC18の [What's New in Testing](https://developer.apple.com/videos/play/wwdc2018/403/) で語られています。

## テストは、デフォルトで名前順に実行される

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/994efe45-9e39-70e7-0a5b-3981c7401298.png">

これを確認するため、以下のようなXcodeプロジェクトを用意しました。

* 整数と、そのインクリメント機能を持つ `Singleton` オブジェクトを用意

```swift:Singleton.swift
import Foundation

final class Singleton {
    static let object = Singleton()

    private(set) var value: Int = 0

    private init() {}

    func increment() {
        value += 1
    }
}

```

* `TestCase1` 〜 `TestCase9` までのテストケースを用意し、以下のようなテストコードを実装

```swift:TestCase1.swift
import XCTest
@testable import ParallelXCTest

class TestCase1: XCTestCase {

    override func setUpWithError() throws {
    }

    override func tearDownWithError() throws {
    }

    func testExample() throws {
        Singleton.object.increment()
        // 1と"TestCase1"の部分はTestCaseXクラスごとに異なる
        XCTAssertEqual(Singleton.object.value, 1, "TestCase1")
    }
}
```

このテストを実行してみると、確かに `TestCase1` 〜 `TestCase9` の順に実行されました。
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/ff895268-f795-d099-cd47-b0a450958d35.png">

## ランダムな順番でテストを実行する

Testターゲットのスキーム設定から `Test > Info > Options...` を開き、 `Randomize execution order` を有効にします。

<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/37f834c1-84fd-cacb-31b1-2b9f4bafdefb.png">

この状態でテストを実行すると、テストケースがランダムに実行されます。
実行順に依存するテストため、このようにほとんど失敗してしまいます。

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/30da74a5-a5f9-8365-d14b-095c88e80b27.png">

## テストを並列実行する

先ほどのスキーム設定で `Execute in parallel` を有効にすると、テストが並列に実行されます。

<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/9ad15e6d-a5dc-6587-e439-a1c81448838c.png">

また先のWWDCセッションにて、並列実行がどのような単位および環境で実行されるか言及しています。

* テストの並列実行は、シミュレータのクローンを複数作成することで実現する
  - 各クローンは元のシミュレータの「複製」であるため、**クローン間でメモリやストレージへの変更は共有されない**
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/c49a5a79-c667-b601-15f4-e52bbc67e053.png">

* テストケース単位で各クローンに分配、あるテストケースが完了したら次のテストケースを実行...という形で実行する
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/4dcce7a0-f5bb-bdbc-fe08-8f5c554b4ff3.png">

`TestCase1` 〜 `TestCase9` を並列実行させてみたところ、このようにシミュレータクローンは4つ作成されましたが、すべてのテストが1つのクローンで実行されてしまいました。

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/82a7afac-d3f7-d59e-77ca-ae6bb80192e4.png">

これはテストコードが軽量すぎたためで、負荷の代わりに以下のようなスリープを入れて再度並列実行させてみました。

```Singleton.swift
final class Singleton {
    ...

    func increment() {
        Thread.sleep(forTimeInterval: 1)
        value += 1
    }
}
```

相変わらずテストは失敗しますが、テストケースが各クローンに分配されて並列実行されていることがわかります。

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/f7882cad-7ce3-e39c-074d-e780766e9991.png">

もうひとつ注目すべきは、`Singleton.object.value` が各クローンごとに `1, 2, 3...` と増え、最後に実行されたテストケースであっても `9` になっていません。これはテストの並列実行が「クローン間でメモリやストレージへの変更は共有されない」ためです。

**各テストケース間や実行順序に依存関係がない** よう、アプリケーション設計やテストケースの実装が重要であることが理解できるかと思います。

ただ別の見方をすれば、`setUpWithError()` でSingletonオブジェクト等の値を変更しても  `tearDownWithError()` で元に戻せば、他のクローンや後続のテストケースへは影響を与えないとも言えます。

XCTestの振る舞いを理解しておけば、広範により良いテストコードが書けるのではないかと思います！