Link and Motivation Developers' Blog

リンクアンドモチベーションの開発者ブログです

GPTを使った新たな開発者体験!? flaky testを自動修復してみた

はじめに

リンクアンドモチベーションでQAエンジニアをしています。

普段は自動テストの保守・運用をメインに活動しております。

今回は、そんな私が最近苦しめられているflaky test との戦いに明るい兆しが少し見えてきたというお話をします!

あ、そんなやり方あるんだと少しでも今後の開発の気づきにつながればと思っています。

flaky testって何?

flaky testは、信頼性のないテストのことを指します。これはテストコードやプロダクションコードに変更がないのに、テストが成功したり失敗したりする不安定な状態です。

第2回 偽陽性と偽陰性 ~自動テストの信頼性をむしばむ現象を理解する~ | gihyo.jp

一般的に、ブラウザを介して実際のシステムをテストするE2Eテストでよく発生します。例えば、サーバーの負荷が高まり、APIのレスポンスが返らないまま画面の要素のチェックを行い、テストが失敗してしまうなど、コード外の要因によって発生することがあります。

しかし、RSpecを使用したユニットテストでもflaky testは発生します。

以下のサンプルコードは、flaky testが発生する例です。

(弊社の一部のサービスはテストフレームワークとしてRSpecを利用しています。)

it 'ユーザーが作成されること' do
  User.create(name: 'Test User')
  datetime = DateTime.now # Mon, 19 Jun 2023 17:45:38 +0900

  expect(
    User.where(
      name: ‘Test User’,
      created_at: datetime,      
    )
  ).to be_present
end

このテストコードの成否は実行タイミングに依存しています。

DBに保存される時刻と現在時刻が異なり、対象のレコードが見つからずテストケースが失敗する場合があります。

今回は、そんなユニットテストにおいて発生するflaky testについて話していきます。

課題

課題は大きく以下の2点です。

  • CIの待ち時間の増加による、生産性の悪化

  • ユニットテストの信頼性低下による、品質の悪化

CIの待ち時間の増加

ユニットテストは、PRを作成したときにCIで実行されるようになっています。

そのため、flaky testが多い状態だと本来通るべきテストケースが失敗し、CIを再実行する必要が出てきます。

(理想的には、発見した人が直すことが望ましいですが、すぐに修正できない場合や時間を割けない場合も多いです。)

その結果、CIの待ち時間が延び、リードタイムにも影響を及ぼします。

CIの待ち時間自体も問題ですし、不要な認知負荷も生じるため、生産性への影響は計り知れません。

ユニットテストの信頼性低下

また、何度もユニットテストのCIが失敗しているようだと、開発者がユニットテストに対して疑心暗鬼になります。

その結果、正しくテストケースが失敗しているにも関わらず、いつものようなflaky testだと勘違いをして 変更をリリースしてしまうなんてこともあるかもしれません。

ユニットテストの価値が失われてしまう第一歩になります。

こういった形で、flaky testの存在はプロダクトの品質・デリバリーのスピード全体に悪影響を与えています。

flaky testの可視化

まずは改善の前に、flaky testの混入を把握するために、以下の内容を可視化しました。

  • 成功率の可視化

  • 優先課題となるテストケースの可視化

masterにマージされた後に実行されるユニットテストを計測対象にしました。

そこでテストケースが失敗した場合は、一度は成功しているがその後に失敗した不安定なテスト = flaky testとみなすことができます。

以下のフローチャートのように結果の可視化を行いました。

flaky testに関するデータの可視化フロー

成功率の可視化

次のグラフはユニットテストのCIの成功率を月ごとに見た結果です。

成功率の月ごとの遷移
可視化を始めた4月時点での成功率がかなり低いことがわかります。

6月には、4月時点と比べてユニットテストのCIの成功率が大幅に改善されました。

改善を進めた方法については、次の章で説明します。

優先課題となるテストケースの可視化

また、flaky testになっているテストケースを発生頻度順に一覧化し、優先課題となるテストケースの可視化をしました。

これにより、flaky testの中でも頻繁に発生しているテストケースを特定し、コスパよく改善することができました。

発生頻度順に並べたflaky test一覧

GPTを用いたflaky testの改善

可視化により、ユニットテストのCIの成功率が低下していることは分かっていましたが、自分自身や開発チームのリソースを割くことが難しく、改善は後回しになっていました。

そんな時に、社内でGPTに関連したアイデアソンが開催されました。このアイデアソンでは、GPTを使用して開発プロセスの効率化に取り組むアイデアを出し合いました。

これをきっかけに、GPTを使用してflaky testの改善に取り組んでみました。

イデアソンの詳細はこちらです。 link-and-motivation.hatenablog.com

コードの改善提案

ChatGPTに対して、flaky testの原因と改善コードを提案してもらいました。

ChatGPTに提案を求めるためのプロンプトは以下の通りです。

# フレーキーなRSpecテストの調査と修正に関する質問:
### 前提条件
・Railsを使った開発です。
・フレーキーなテストとは、コードまたはテストそのものに変更がないにもかかわらず、合格と不合格の両方を返すテストのことです。
... etc

### 質問
- テストがフレーキーになっている原因と対策をコードベースで提案してください。
- わからない部分があれば私に質問してください

### 過去にフレーキーなテストが発生した事例
- 実行時間に依存することで落ちる
- 月初のタイミングで落ちる
...etc

### エラーログ
{{ エラーログ }}

### テストコード
{{ テストコード }}

### プロダクションコード
{{ プロダクションコード }}

例えば、次のような実行タイミングに依存するテストコードがflaky testとして存在していました。

      it 'メール送信予約レコードが生成されていること' do
        post_pre_tests_send_mail
  
        datetime = CustomDateTime.get_date_time_hash
        date = datetime[:date].to_date
        time = datetime[:time]

        send_company_users.each do |send_company_user|
          expect(
            SendReservation.where(
              company_id: send_company_user.company_id,
              user_id: send_company_user.user_id,
              mail_type_id: MailType.type_ids[:pre_test],
              day: date,
              time: time,
              is_moved: 1,
            ),
          ).to be_present
        end

このコードに対して、ChatGPTからは以下のような修正提案が得られました。

ChatGPTを使ったflaky testの改善提案

この提案されたコードを適用すると、実際にflaky testを改善することができました。

一発で回答が得られる場合は半分以下でしたが、いくつかのflaky testを迅速に改善することができました。

コードの自動修復

先ほどのプロンプトを参考にして、自動修復の試みも行いました。

これは人間の介在なしにflaky testを検出し、コードを修正することを目指したものです。

以下のような構成で自動修復を試みました。

  • テストが失敗するまで、テストの実行
  • 失敗した時のエラーログとテストコードを元にプロンプトを作成
  • GPTに問い合わせ
  • 問い合わせ結果をもとにコードの修正
  • テストの再実行をし、コードの検証

自動修復の構成

以下は、自動修復の実行中の様子です。

GPTを使ったflaky testの自動修復

まだいろんなテストケースへの適用はできていませんが、提案の精度を向上させることで、開発者体験に大きな変化をもたらせる可能性を感じることができました。

今後の展望

前提として、GPTの活用はまだまだ正答率は低く、これだけでflaky testの問題が完全に解決できるわけではないと思っています。 そのため、GPTによるコードの改善ももちろんですが、運用の仕組み自体のアップデートも必要になると考えています。

flaky testの運用の仕組み化

改善ポイントは、大きく2つです。

まず1つ目は、既存のflaky testを改善した後、新たに発生したflaky testがあれば、自動的に担当者を割り当てる仕組みを構築していきます。

自分自身がバグを発見した場合には、インシデント対応に取り組むことが一般的ですので、テストに対しても同様のアプローチを取ることで、運用に組み込むことができると考えています。

2つ目は、テスト成功率の目標値の設定と浸透です。

flaky testの存在を完全に無くすことは難しいと思いますが、テスト成功率99 %くらいになれるようにしたいです。 そのためには優先順位を上げて対応をする必要があると思うので、目標値の設定と開発チームと認識合わせて改善していきます。

自動修復の改善

先ほどの自動修復を発展させることで、さらなる生産性向上に貢献できると考えています。

改善ポイントは、大きく2つです。

まず1つ目は、問題が発生した時点で修正内容をPRとして作成し、レビューを依頼する部分までを自動化することです。精度が高まれば、即座にマージすることも検討できるかもしれません。

2つ目は、改善提案の精度向上です。現時点では一部のコードにしか適用できていませんが、今後flaky testの改善に関する知識が蓄積されていくはずです。そのため、「過去のflaky testが発生した事例」の情報に知識を加えることで、類似の問題に対しても改善策を提案できるようになると思います。

最後に

GPTの取り組みをしていく中で、こんな世界あるかもという期待を少しでも身近に想像できて、ワクワクしました。

現在、私はイネーブリングチームに所属しており、各プロダクトの開発チームの生産性を向上したり、 支援する立場にあります。

開発者の皆さんが、人でなくてもできる作業に時間を割くことなく、お客様や技術に向き合う土壌を作れるように。

そのために、劇的な変化を届けて新しい開発者体験をともに創れるように尽力していきたい、と思いを新たにしました。