CircleCIの「失敗したテストのみ再実行する」機能とテストの並列実行

こんにちは。CircleCI カスタマーサクセスチームのChisatoです。

最近個人的に幸せを感じた出来事は、近所でかわいい子犬（ボーダーコリー）に鬼絡みされたことです。
久しぶりに「ずっとこの瞬間が続けばいいのにな」という気持ちになりました。

逆に、「とっとと終わってほしいな」と思うのが長いテストの実行時間です。
今回は、そんな悩みを解決するのに役立つ下記の機能についてご紹介します。

* 人気の新機能「失敗したテストのみを再実行する」（Rerun failed tests）
* テストの分割と並列実行（Test splitting and Parallelism）
* Test Insights（テストインサイツ）

# 「失敗したテストのみを再実行する」とは
これまで、テストを再実行するときの選択肢は、次の3種類でした。
* Rerun from Start （最初からすべて再実行）
* Rerun from Failed（失敗した箇所以降のジョブをすべて再実行）
* Rerun with SSH

失敗した箇所以降のジョブをすべて再実行すると、「再実行しなくても良い部分」まで実行されるため、結果を得るまで時間がかかったり、無駄にクレジットを消費してしまったりします。

「失敗したテストのみを再実行する」機能を使うと、迅速に結果が得られ、無駄なクレジットの消費も防ぐことができます。

おかげさまで、多くのお客様から「この機能はぜひ活用したい！」と良いフィードバックをいただいております。

それではテストの並列実行から、実際の設定を見ていきましょう。
<br>

# テストの分割と並列実行の設定

今回はドキュメンテーションページの[サンプルプロジェクト](https://.com/ryanpedersen42/circleci-react-test-splitting "github.com/ryanpedersen42/circleci-react-test-splitting")を参考にしました。
このプロジェクトには既にテスト分割と並列実行が設定されていますが、設定方法を説明します。

```yaml:Config.yml file
version: 2.1

orbs:
    node: circleci/node@3.0.1

jobs:
    build-and-test:
        docker:
            - image: cimg/node:12.16
        parallelism: 5  #1
        steps:
            - checkout
            - node/install-packages:
                pkg-manager: yarn      
            - run: mkdir ~/junit #2
            - run:
                name: Test application
                command: | #3
                    TEST=$(circleci tests glob "src/__tests__/*.js" | circleci tests split --split-by=timings)
                    yarn test $TEST　
            - run:
                command: cp junit.xml ~/junit/  #4
                when: always
            - store_test_results:   #5
                path: ~/junit
            - store_artifacts:
                path: ~/junit
workflows:
    build-and-test:
      jobs:
        - build-and-test

```

次の手順の番号は、コードスパン中のコメント番号と対応しています。

1\. テストを分割し、並列実行するため、`build-and-test` ジョブで `docker` キーの後に `parallelism` キーと 値 を指定します。例えば、値を 5 と設定すると、5 つの別々の Docker コンテナがスピンアップされます。
2\. テストの分割方法には３種類ありますが、最も効率的なのが実行時間を元にした分割です。これを有効にするには、テストデータをアップロードする必要があります。テスト結果は `junit.xml` に保存されるため、 `- run: mkdir ~/junit` を記述します。
3\. テストファイルと分割方法を設定します。環境変数 `$TEST` の中で、`circleci tests glob` コマンドで、対象のテストファイルを指定します。それらのファイルを `circleci tests split` に渡します。タイミングで分割したいので、 `--split-by=timings` フラグを用います。次に、`yarn test $TEST` で、環境変数`$TEST`を参照してテストを実行するよう設定します。
4\. `command: cp junit.xml ~/junit/` の記述は、テスト結果を `~/junit` に保存するためのものです。毎回このステップが実行されるように `always` とします。
5\. 最後に`store_test_results` のステップを追記することで、テストデータが CircleCI にアップロードされます。このテストデータにアクセスするには、ダッシュボードでジョブを表示し、 `Tests` タブをクリックします。

これで、タイミングによってテスト分割できるようになりました。


### テスト分割の例
テスト分割されると、ダッシュボードには下記のように複数のコンテナが表示されます。
![Screenshot 2023-07-29 at 13.24.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/a06ac786-ef02-4c18-e635-839bc0477742.png)

`TIMING` タブでは、タイミングによって分割されたジョブの各コンテナの情報が確認できます。
![Screenshot 2023-07-29 at 13.33.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/5a5587ea-77cb-ef87-8a7f-9c92937c298f.png)


### テストデータの表示例
テスト結果が正しくアップロードできていると、ダッシュボードの `TESTS` タブを開いたときに下記のような画面になります。
<img width="550" alt="Screenshot 2023-07-29 at 13.26.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/be83e006-97d3-1868-f051-be688cf4fa80.png">


### テスト分割の方法について
テスト分割の方法は大きく分けて３種類あります。詳しくはドキュメンテーションページをご参照ください。
* [名前に基づいた分割 (デフォルト)](https://circleci.com/docs/ja/use-the-circleci-cli-to-split-tests/#split-by-name)
* [タイミングデータに基づいた分割](https://circleci.com/docs/ja/use-the-circleci-cli-to-split-tests/#split-by-timing-data)
* [ファイルサイズに基づいた分割](https://circleci.com/docs/ja/use-the-circleci-cli-to-split-tests/#splitting-by-filesize)

<br>

# 失敗したテストのみを再実行する（Rerun failes tests）機能の設定
下記は、この機能を有効にするために、先ほどのコードを書き換えたものです。

先ほどの `Config.yml file` の状態だと、ダッシュボードから再実行しようとするとき、Rerun failed tests が選択できません。
<img width="500" alt="Screenshot 2023-07-29 at 13.38.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/db0e29b2-6537-3f25-18a0-b72d68cb93ae.png">


```yaml:Config.yml file
version: 2.1

orbs:
    node: circleci/node@3.0.1

jobs:
    build-and-test:
        docker:
            - image: cimg/node:12.16
        parallelism: 5
        steps:
            - checkout
            - node/install-packages:
                pkg-manager: yarn      
            - run: mkdir ~/junit
            - run:
                name: Test application
                command: |  #1
                    TEST=$(circleci tests glob "src/__tests__/*.js") #2
                    echo "$TEST" | circleci tests run --command="xargs yarn test -o junit_family=legacy --junitxml=junit.xml" --verbose --split-by=timings 
                    
                         
            - run:
                command: cp junit.xml ~/junit/
                when: always
            - store_test_results:
                path: ~/junit
            - store_artifacts:
                path: ~/junit
workflows:
    build-and-test:
      jobs:
        - build-and-test

```

1\. 機能を有効にするには、この部分を変更する必要があります。テストの実行には、`circleci tests split` の代わりに、`circleci tests run` を使います。

2\. パラメータの説明は、[ドキュメンテーション](https://circleci.com/docs/ja/rerun-failed-tests/#example-config-file-after)を下記引用します。
>circleci tests run --command="xargs pytest -o junit_family=legacy --junitxml=test-results/junit.xml" --verbose --split-by=timings
>circleci tests run を呼び出し、--command= パラメーターに元のテスト実行コマンド (pytest) を指定しています。 このパラメーターは必須です。 同じく、xargs も必須です。
>--verbose は circleci tests run 用のオプションパラメーターであり、詳細なデバッグメッセージを出力します。
>--split-by-timings は、circleci tests run でタイミングに基づくインテリジェントテスト分割を利用するためのパラメーターです。 circleci tests run を使う場合、このパラメーターは必須ではありません。 テストジョブで CircleCI のテスト分割機能を使わないのであれば、省略してください。

ドキュメンテーションでは、各テストタイプに応じた例を確認できます。

設定変更のおかげで、失敗したテストのみを再実行する機能が選択可能になりました。
<img width="550" alt="Screenshot 2023-07-29 at 13.48.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/20aa9f3f-5418-9e61-ce6e-e950a9cd3df1.png">

実際に機能を使ってみると、失敗した部分だけが再実行されていることがわかります。
![Screenshot 2023-07-29 at 13.48.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/56a98da7-2aaf-4576-5de7-984f5b798d42.png)

最後は気持ちよくグリーンビルドで締めます🥝
![Screenshot 2023-07-29 at 13.54.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/22679a3b-a792-a263-fac2-1d11afb87312.png)




# Test Insights の活用

テストデータをアップロードする設定が有効な場合、テストインサイツ（Test Insights）を利用することができます。
Test Insights では、実行時間の遅い順、失敗の多い順、さらに不安定なテストの自動検知結果を確認できます。

ダッシュボードの `TESTS` タブをクリックすると、直接 Test Insights のページに遷移してテストの分析ができます。
![Screenshot 2023-07-29 at 13.28.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/aae04131-4fcf-03e6-1f12-b71dfdd8e652.png)


下記は「不安定なテストの自動検知機能」の表示例です。
この機能は、多くのお客様にテスト最適化のため活用いただいています。
![Screenshot 2023-07-28 at 16.18.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/b8b4227b-3746-eb20-8018-a88e395dd276.png)


不安定なテストの自動検知はテスト最適化を進めているお客様にたいへん人気の機能です。
下記のOrbを組み合わせると、自分の指定したSlackのチャネルで通知を受け取ることができます（このOrbはソリューション・エンジニアのTadashiさんが作成したもの）。

https://circleci.com/developer/orbs/orb/tadashi0713/flaky-tests-notify

また、下記のプロジェクトを活用すると、Insights API を使って（こちらはサポートエンジニアの Aaronさんが作成したもの）指定したSlackチャネルに定期的にデータを飛ばすことができます。

https://github.com/ogii/circleci-insights-slack

## まとめ
今回のプロジェクトの設定では Jasur さんに「失敗したテストのみを再実行する」機能の設定で助けていただきました。ありがとうございました。

暑い日が続きますが、ご自愛ください。爽やかなグリーンビルドで素敵な夏を過ごしましょう。

技術的なご質問は、お気軽に [サポートセンター](https://support.circleci.com/hc/ja) までお問い合わせください。


## 参考文献
[テスト分割によるパイプラインの高速化](https://circleci.com/docs/ja/test-splitting-tutorial/)
[失敗したテストのみを再実行する](https://circleci.com/docs/ja/rerun-failed-tests/#example-config-file-after)
[テストデータの収集](https://circleci.com/docs/ja/collect-test-data/)