GoogleのJohn Micco氏によるFlakyなテストとその判別方法の解説 #JaSST

はじめに

と書い(てしまっ)たので、JaSST'18 Tokyoに参加した私なりのFlakyの解釈を書きます。

JaSST'18 Tokyoについては以下のページを参照してください。

JaSSTソフトウェアテストシンポジウム-JaSST'18 Tokyo

お知らせ

この記事の内容を4/11に発表しました!

nihonbuson.hatenadiary.jp

発表スライドはこちら

speakerdeck.com

Micco氏のお話

  • John Micco氏(以下、Micco)の話は、以下の3回聞きました。
    • ICST基調講演(2017年)
    • JaSST'18 Tokyo基調講演(★今回)
    • JaSST'18 Tokyoチュートリアル(★今回)
  • それぞれのタイミングで、特にFlakyの話の理解度が変わっていったので、Flakyを中心にMiccoのお話をお伝えします。

いいだろー!*1と言いつつも、t_wadaさんのように後悔した人にも伝われば幸いです。

ICSTでの話

  • Miccoは昨年行われたICST2017でもGoogleの自動テストについて講演されています。
  • その時の説明は、以下の記事を参照してください。

nihonbuson.hatenadiary.jp

基調講演「Advances in Continuous Integration Testing at Google

  • 上記のICSTから追加・変更した内容を中心に記載します。

講演資料

Advances in Continuous Integration Testing at Google

テスト文化について (3ページ付近)

  • テスト文化を大切にしている
    • テストのテクニックはトイレに貼ってある
    • Google Testing blogで発信している
    • GTACでも発信している
  • テストが書けることを期待している
    • 新規採用の際はテストが書けないとダメ
    • Googleの新入社員に対してテストの仕方を教える
  • SETIエンジニアを各開発チームに配置している
  • モデルテストなどは実験をしている
  • 大体はエンジニアが書いた自動テストが回っている
  • 自動化テストを促進する文化がある
    • 10年の文化がある
  • コードビューがある
  • 450万のテストは開発者が行っている

回帰テスト (4ページ付近)

  • 1つのbranchで何十億ものコードがある
  • それを3万人の開発者がテストをする
  • どの回帰テストをするのかの選択が大切
    • これを選択する仕方が大事
  • 現在は2秒に1回テストが動いていく
  • 全ての変更リスト(CL)は2年間保管している。
  • これだけのデータがあれば、パターンが見えてくる
  • 明日のチュートリアルではラボを見ていく

Milestone Scheduling (8ページ付近)

  • 一定期間のマイルストーンで区切る
  • ここまでの変更の累積からテスト対象を決める
  • スケジューリングして実行する
  • NGになった場合、原因探し(犯人探し)を行う
  • NGだったものがNGになるものよりも、OKからNGになる(状態の遷移が起こる)テストの方が興味がある
    • 99.8%は状態の遷移が起きない
    • 0.2%のテストのみをスケジューリングしたい
      • コンピュータリソースを改善するため
    • ほとんどの問題はこの0.2%に集約される
  • 依存性ベースでやっているのは上手く行かない*2
    • すべてが依存しているものがあるから

RTS Affected Target Counts Frequency (13ページ付近)

  • 頻度のグラフを示した
  • コード変更の半分は、400万のテストケースの中の38のテストケースに影響する
    • すべてのテストを流し続ける必要はない
  • 何らかの方法でスケジューリングを減らさないといけない

Project Status and Groupings (16ページ付近)

  • リポジトリは1つだけだが、プロジェクト別にグループ分けをしている
  • GmailにもSearchにも一連のテストが紐付けられている
    • Searchで失敗していても、Gmailのリリースはできるようになっている
  • すべてのテストを見落としていない限り、Greenの結果にはならない
  • オーバースケジューリングを減らすには、Greenに近い確信レベルが必要
  • 実行していない自動テストは、決定的でない代わりに、確率論を用いる(スライド17ページ)
  • チームとしてはリスクを持ってリリースする

Safe Results (33ページ付近)

  • Skipしても、その前後でテスト結果が変化していないならばSkipして問題なかったと判断している
  • Unsafeな場合、どのコミットが原因なのか探さないといけない
  • 最終的にはマイルストーンを排除していきたい
  • 90%以上はテストをしなくても、きちんと動くものをコミットしている
  • 遷移が分かっているものはSkipする
  • Skipする割合とテスト結果の変化は直線的ではないことがわかった

Flaky (35ページ付近)

  • あてにならないテストのこと。
  • Flakinessは同じコードで成功と失敗の両方を観測されるテストのことをいいます。*3
  • 全体の16%はFlakyである
  • Flakyテストを再実行すると、リソースの2%〜16%を費やすことになる。

分析結果

  • 変更が多いファイルはFlakyが起きやすい
  • 開発者は1人よりも2人のほうが良い
  • 言語によってはFlakyが起こりやすい
  • C++ < Java < Go でFlakyが起きづらい
  • データセットにクエリをかけて、論文の結果を検証・再現するのは明日のチュートリアルで実施予定
  • リソースを待っていたり、WebDriverのテスト、スリープが入るテスト、マルチスレッドのテストはFlakyが起きやすい
  • テストの合否を記録して、時間によるテスト結果の遷移が多いのはFlakyなテストと言える
  • 816件のFlakyなテストがあった
  • 詳しくは明日のチュートリアルで!

質疑応答

その場でツイートしていたので、詳しくは以下のまとめを見てください。

togetter.com

その中から3つだけピックアップ

色々な質問の中で、手動で行っている話はこの3つだけであり、他は徹底的に自動化しているという印象を受けた。

基調講演の感想

  • ICSTの頃に比べ、どのようにすれば少ないリソースで効率的に不具合を見つけられるテストを行えるか考えられていた。

    • ICSTでは、依存関係から判断する話はあったが、テストをSkipする話をそこまで伝えていなかったので…。
    • 以下の文章ぐらい?
      • 例えば、テストが成功ばかりしている場合、そのテストは必要ないのかもしれない (ICSTの聴講レポートより)
        • ICST当時は、「不具合を見つけられないテスト」と解釈していたが、今となっては「本来Skipすべきだったテスト」という意味で必要ないと言っていたのかも。
  • Flakyなテストというのが「過去のテスト結果とも照らし合わせて判断するもの」という意味で使われている(とこの時点では感じた)のは、去年のICSTでの話と異なっている点だと感じました。

    • 「FailedになるとFlakyになるか確認する」という文章が、「テストをリトライした結果を見てFlakyか判断する」「(過去のテスト結果ではなく)リトライした今のテスト結果を見て判断する」という風に捉えていたので…。
    • ICSTとJaSST基調講演で定義が異なるという疑問は、この後に記載しているチュートリアルを受講したことで、払拭されていくわけですが…。

チュートリアル「How to identify test flakiness in your test result data」

  • チュートリアルの内容をどこまで載せてよいのか悩みましたが、Miccoと連絡を取り、ブログに載せても良いという許諾を頂きました。
    • Thank you, Micco!

概要

  • Googleでのデータを用いて、どのようにFlakyであるか判別するのかを体験しました。
  • 体験時に利用したSQLは以下のGithubページにあります。

github.com

  • ここにコミットされているSQLGoogleのBigQueryを用いて実行しました。

  • チュートリアル中のツイートは以下にまとめました。

togetter.com

対象データ

  • Fullデータは以下の通り
    • 143万件のテストケース
    • 5億件のテスト結果
  • 「数が多いので、もっと数を絞ってシンプルなデータを用意した。」by Micco*4
    • 1万件のテストケース
    • 340万件のテスト結果

テスト結果の種類

  • 以下のようにテスト結果を分類している。
    • PASSED…テスト成功
    • FAILED_TO_BUILD…ビルド失敗
    • FAILED…テスト失敗
    • INTERNAL_ERROR…テスト開始の準備が整わなかった
      • 例えば、ブラウザが立ち上がらなかったなど
      • インフラチームが責任を持っている部分
    • PENDING…実行開始しようとしたけどできなかった
    • ABORTED_BY_TOOL…開始したけど制限の15分を超過したので強制終了した *5
    • FLAKY…テストが失敗し、そのテストをリトライしたら成功したもの
      • ここでのFLAKYは、この後に話しているFlakyと別の使い方をしているように思えるので注意!
  • テスト結果を細かく分けることで、以下の点をハッキリさせている。
    • 成功・失敗だけでなく、どんな失敗なのか
    • 失敗の原因はどのチームが責任を持つべきなのか

テスト結果のEdgeを元に、Flakyなテスト(候補)を特定する

  • テスト結果をさらに以下の4種類に分ける。*6
    • Keep Succeed…前回も今回も成功しているテスト
    • Keep Failed…前回も今回も失敗しているテスト
    • Positive Edge…前回は失敗し、今回は成功したテスト
    • Negative Edge…前回は成功し、今回は失敗したテスト

f:id:nihonbuson:20180310105503p:plain

  • 基調講演でも話したように、我々が興味を持っているのは遷移が発生している(Edgeの)部分である。
  • Edgeの中の84%はFlakinessである。
  • これらはすべてのテストの16%に集中している。
  • より多くの遷移に発生しているのはFlakyである。
    • これらはSQLを引っ張り出すことができる
      • 同じテストケースの前回結果と今回結果が変わったどうかでカウントをとる
    • 一番多いテストケースで、1ヶ月に884回も遷移が発生した。
  • 300万ケース中7590ケースで1ヶ月に4回以上の遷移が発生した。
    • これ以下のテストケースはFlakyではない、つまり純粋なテスト失敗の可能性が高い
  • PASS/FAILを記録するだけでもFlakyの判断をすることができる

テスト結果のパターンを元に、Flakyではないテストを特定する

  • Flakyなテストケースはランダム性がある。
  • Flakyではないテストケースはパターンが存在する。
  • 下記の図の場合、「t4とt6」や「t2,t3,t5,t7」は同じパターンで成功や失敗を遷移している。
    • このような組はFlakyではないと考えられる。

f:id:nihonbuson:20180310105623p:plain

  • 過去1ヶ月間のテスト結果のデータを解析すると、以下のことが分かった
    • 最大5000ケースが同じパターンだった
      • これらはFlakyではない
      • これらが同じタイミングで成功・失敗が遷移するとなると、ライブラリ起因の失敗などの可能性が高い
    • 同じパターンのテストケース数が2つ以下のものが、Flakyの疑いのケースだった。

おまけ:テスト失敗の分類をする

  • Flakyかどうかを判別することで、純粋な失敗のテストケースについて分類をすることができた。
  • どんな拡張子がテストの失敗を起こすのか
  • 誰がテストの失敗を起こしやすいのか
    • ただし、これは匿名化している。
      • これを人事情報を結びつけないようにするため。
      • これらは開発者にFBするのではなく、解析したものを機械学習に投入するために使っている
  • Flakyはテストコードとプロダクトコードのどちらを指すのか?
    • どちらに原因があるかを区別していない
    • よく聞かれる質問です。

チュートリアルの感想

  • Flakyを色々な使い方で言っていることが分かった。
    • 1つのテストケースを(コードの変更もせずに)リトライするとテスト結果が変わる場合
    • 前回のテスト結果と今回のテスト結果が違う場合
      • 特に、他のテストケースとテスト成功・失敗のパターンが異なる場合
  • Flakyの判別は特殊な操作をしているのではなく、単純なクエリで推定できることが分かった
    • ただし、Googleは推定するためのデータ数がとてつもなく多いのでできる部分もあるけど
      • 自動テストをどんどん増やす方向になっていくと良いメリットの1つともいえる

おわりに

という、Micco尽くしのJaSST'18 Tokyoでしたが、おかげでFlakyとその判別方法について少しだけ近づけた気がします。

  • 去年のICSTでは、「1つのテスト結果の中で、リトライするとテストが成功したり失敗したりするもの」がFlakyだと思っていました。
  • 今回のJaSSTの基調講演では、「前回のテストと今回のテストの結果が違うもの」がFlakyだと思いました。
    • あれ?Micco、定義が変わってない?と思いました。
  • 今回のJaSSTのチュートリアルで、「1つのテスト結果でも複数回のテスト結果でも結果が変わるもの」をFlakyだと理解できました!

  • 大満足なJaSSTとなりました!

関連記事

  • Miccoも参加したパネルディスカッションについても記事にしました。こちらもどうぞ。

nihonbuson.hatenadiary.jp

*1:t_wadaさんに対してこんな風に言える数少ない機会

*2:ここは私の理解が曖昧です。依存性ベースというのがどういうことを指しているのか…

*3:ここでの「同じコード」というのは、おそらく「同じテストコード(プロダクトコードに変更はあった)」ともとれるし「両方とも同じ」ともとれる。ここが(私個人の)混乱の原因と分かったのは、チュートリアルを受講した後だが…。

*4:いや、これでも十分多いっす…

*5: @yuki_shiro_823 さんと @kz_suzuki さんにご指摘いただきました。ありがとうございました!

*6:実際に説明で述べているのはPositive EdgeとNegative Edgeだけで、それ以外の2つは区別するために勝手に命名しました。