CLOSE

望ましい自動テストとは: どのようなテストが開発生産性と開発者体験を共に高めるのか(全4記事)

和田卓人氏が考える“自動テストの真の目的”とは? コスト削減ではなく「変化に対応する力」を得るためのベストプラクティス

ソフトウェアエンジニアリングの第一人者である和田卓人氏が、自動テストの本質と望ましい姿について語りました。コスト削減ではなく「変化に対応する力」を得るための自動テストの重要性や、信頼性の高い自動テストを実現するための具体的な方法論を解説。まずは、偽陽性や偽陰性の罠を避け、持続可能なソフトウェア開発を実現するための洞察に満ちた講演内容を紹介しました。全4回。

和田卓人氏自己紹介

和田卓人氏:よろしくお願いします。

(会場拍手)

お招きいただきましてありがとうございます。「望ましい自動テストとは」というタイトルで、自動テストに関するお話をさせていただきたいと思います。和田卓人と申します。インターネット上ではだいたい「t-wada」さんと呼ばれていて、t-wadaアカウントが下にいろいろ並んでいるんですが、ソーシャルアカウントをいろいろやっていますという感じです。

本日の講演、私の講演はいつもスクリーンショットとかバンバン撮って、「X」にバンバンポスト(ツィート)していただいてかまいません。ハッシュタグは「#dxd2024」と「#SP」ですね。ダブルタグという感じでガンガンポストしていただけると盛り上がって良いなと思います。どんどん写真撮っちゃってかまいません、公開してかまいませんというようなレギュレーションでお話をさせていただきたいと思います。

自動テストの目的

じゃあ、最初に序論のところで、自動テスト。自分たちでコードを書いて、テストを書いて動かしていくみたいな、自動でテストをしていく仕組み、自動テストを、なんで書くんだろうかっていうところのつまずきから一番最初にいきたいと思います。

アンチパターン。よかれと思ってどつぼにはまるパターンを最初にまず挙げておきます。1つ、テストを自動化するのをコスト削減のためにやるというのは、これはアンチパターンです。

だいたいすごく多いんですよね。コスト削減のためにテストを自動化しますと。結果として削減される面はあるんですけど、コスト削減を目的にしちゃうと、短期的には自動テストの学習コストがのしかかってきます。中長期的には自動テストの保守コストがのしかかってくるので、結果、「あんまり、なんかコスト削減効果ないね」みたいな感じで手動テストに戻ってしまうみたいな現場を、私はたくさん見てきました。

仕事として、私はコンサルタントや技術顧問という肩書でいろんな現場を見てきたんですが、戻ってしまうというような現場は、たくさんありましたというような感じですね。

なので、ネジを巻き直さなきゃいけないと。私の知り合いの「ところてん」さんがこんなポストをしていました。

「なんとなく、DXという言葉に対しての引っかかりが理解できてきた気がする」と。「『デジタル』という言葉に『アジリティ』という意味が含まれていないので、『変更容易性の高いソフトウェアによるアジリティの獲得』というDXの本質がソフトウェアエンジニア以外には伝わってない感じなんだな」ってなっていて、これはとてもいいポストだなと思っています。

DXのD、「Digital」という言葉にアジリティっていう言葉が含まれていないので、自分たちのソフトウェアを変更容易性の高い状態にすることによって、社会とか世の中の変化にソフトウェアを変化させることでついていくというような本質が伝わっていないんだな、というかたちになってしまうんですよね。

というので、変化についていく、変化を可能にするっていうことが、とてもとても大事なんですよね。自動テストもそういった変化を可能にしていくための大事な技術プラクティスの1つのピースであるというふうに考えています。

『Googleのソフトウェアエンジニアリング』っていうとてもいい本があるんですけど、ここに「なんで自動テスト、書くんだろうね?」「変化を可能とする能力を備えておくため」という表現があります。変化をソフトウェア開発の本質的特性として受け入れることができる。ソフトウェアっていうのは変化してなんぼだっていう話なんですよね。ソフトウェアはソフトじゃなきゃいけないんですよ。よくボブおじさんも言っていますけどね。

なんだけど、保守性が下がってしまってソフトウェアを変えるのが、とてもじゃないけど難しくハードになってしまうみたいな状況があり得てしまう。これが悲しい。スクリーンショット推奨ページはこのページですね。

なぜ、自動テストを書くんでしょうか? よく「コストを削減するため」と言いがちなんですけど、これはNGです。だいたい、あんまりうまくいきません。じゃあ、なぜ自動テストを書くんだろうか? 素早く躊躇なく変化し続ける力を得るために、自動テストを書いていくんですという話です。これがもう最初っから今日の1つの結論みたいなものですね。このために、どうやっていこうっていう話です。

ということで、もう1つ結論ページがあります。じゃあ、望ましい自動テストとはどういうものだろうか? こういうものだろうと考えています。このページもスクショ推奨ページです。読み上げましょう。

自動テストの目的。これは私が考える自動テストの目的です。「信頼性の高い実行結果に短い時間で到達する状態を保つことで、開発者に根拠ある自信を与え、ソフトウェアの成長を持続可能にすること」。これが、私が考える自動テストの目的です。この目的に至る道というのを、今日は分解して一つひとつ積み上げていきましょうというような話になります。

分解すると、それが今日のアジェンダになります。望ましい自動テストの姿。信頼性の高い実行結果に短い時間で到達する状態を保つことによって、ソフトウェアの成長を持続可能にすることですね。ひいては、事業の成長を持続可能にすることです。じゃあ、1個1個ひもといていきましょうというかたちになります。

信頼性の高い自動テスト

信頼性の高い自動テストとは何か? 信頼性の高い自動テストというと、おなじみの『LeanとDevOpsの科学』という本からひもといていきましょう。この本は、もう必読書と言っていいでしょう。

ここの65ページ目にこんなことが書かれています。「テストの自動化において、ITパフォーマンス(企業の業績ですね)の予測尺度となりうることが判明したのは次の2つである」。この2つに関しては、統計的に有意なかたちで影響があったっていう話ですね。

「1つ、信頼性の高い自動テストを備えること。2つ、開発者主体で受け入れテストを作成・管理し、手元の開発環境で簡単に再現・修正できること」とあります。今日はこの中でも、特にこの1つ目、「信頼性の高い自動テストを備えること」にフォーカスします。

「信頼性の高い自動テストって何だ?」っていう話ですね。「信頼性の高い自動テストとは何か?」、ざっくり言うと嘘がないことです。嘘がないから信じられるという話ですよね。

嘘があるとはどういうことか。嘘があるというのは、2つあります。1つ、偽陽性というやつがあります。2つ目、偽陰性というやつがあります。この2つがあると信じられなくなる。

信じられるとどうなるかというと、テストに成功していれば、「リリースとかデプロイができるね」、テストに失敗していれば、「自分たちのコードに直すべき場所があるね」という2択の状態に自分たちの行動を制限することができる。これが生産性が高く、開発者体験が高い状態っていう感じですね。

嘘は、2種類ありました。「偽陽性:false positive」の誤検知っていうのは、例えば火災報知器にたとえるなら、火が出ていないんだけど火災報知器が鳴るような状況です。自動テストにおいてはどういう状況かっていうと、コードが悪くないのにテストが失敗する状況。よくありますよね。

みなさんにも経験があると思います。なんか不安定なテストがあって、不安定なテストは失敗するけど別に誰も悪くなかった。ただ単に実行が不安定とか遅いとか何らかの状況によって、コードはまったく悪くないのにテストが成功したり失敗したり不安定になってしまうと、オオカミ少年みたいになって信じなくなってしまう。

もう1つ、偽陰性のほうは見逃しです。火が出ているのに火災報知器が鳴らないみたいな。コードにバグがあるのにテストが失敗しないという状況。

この2つが多いと疑心暗鬼になってしまって、成功すればデプロイ、失敗すればコード修正という2択の状態に持ち込めないんですよね。なので、この信頼性というのを維持するという必要があります。この信頼性を維持する努力っていうのは、かなり払い続けなきゃいけないんですけど、払い続けるだけの価値がある。

偽陽性と偽陰性のマッピング

ということで、2×2(ツーバイツー)にまとめましょう。偽陰性と偽陽性というやつはどこにマッピングできるかというと、プロダクトコードが「正しい」「誤っている」と、テスト結果が「成功」「失敗」の2×2だと、「プロダクトコードが誤っているけど、テストが成功してしまう。火が出ているのに火災報知器が鳴らない」とか、「火が出ていないのに火災報知器が鳴る。プロダクトコードが正しいのにテストが失敗してしまう」というような状況。

この2つを可能な限り減らしていけば減らしていくほど、自分たちの開発者体験、生産性が高まるという話です。なので、継続的な努力と投資には価値があるという話になるんですね。

偽陽性の典型的パターン

じゃあ、そういった偽陽性と偽陰性っていうのは、どんなような典型的なパターンがあるんだろうかというと、偽陽性のほうの火が出ていないのに火災報知器が鳴るパターン、テストコードが悪くないのにテストが失敗するパターンっていうのは、大きくは2つあります。fragile testとflaky testです。

特に有名な2つ目のほうの信頼不能テスト「flaky test」というやつを挙げましょう。例えば「テストコードからブラウザを自動で動かして画面遷移しながらボタンをクリックしていって」みたいなタイプのエンドツーエンドテストとかによくあるものです。

信頼不能テスト、flaky testの定義は何かというと、プロダクトコードとかテストコードとかそういったコードに一切手を触れなくても、結果が成功だったり失敗だったり不安定なもの、これをflaky testといいます。みなさんもたぶん経験があるんじゃないかなと思います。

flaky testがたくさん出てくると、テストの失敗に対して、みんな鈍感になってしまって、「flaky test、1個失敗したらもう1回リトライすれば動くよ、たぶん」みたいな感じで、「成功するまで3回リトライしてみよう」みたいな運用になってしまうといった、すごいよくあるパターンですね。

というので、これ、可能な限り減らしていきたい。コードが悪ければ失敗する、コードが良いなら成功するというようなかたちに持っていかなきゃいけない。

どのくらいまで減らさないといけないかというと、先ほど出てきた『Googleのソフトウェアエンジニアリング』っていう本においては、テストスイート全体の1パーセントがflakyになると、エンジニアはテストの結果をだんだん信じなくなり始めると言われています。なので、可能な限り減らす。

flakyであると認識したら、運用レベルではどうやっているかっていうと、「flaky」というタグをつけて隔離します。

じゃないと、要するに信用できるテストと、時々信頼できないテストがある。時々信頼できないっていうのを、人間も機械も認識しておかなきゃならないのでflakyタグをつけて、flakyタグは隔離スイート、メインのCIのラインとは別のところで動かして、安定して動くようになったらflakyを外してメインに戻すとか、そんな感じの運用をしたりするというようなかたちですね。じゃないとオオカミ少年がたくさん出てしまうという感じです。

もう1つ、fragile testとかbrittle testと呼ばれる脆いテスト。これも偽陽性のよくあるパターンです。

先ほどのflaky testは、手を触れなくても失敗したり成功したりするテストのことでした。脆いテストのほうは、手を触れるとすぐに失敗するテストのことです。これもみなさん、たぶん経験あるんじゃないかなと思います。

モックだらけのテストとか、あるいはプライベートメソッド、思いっきり触れてしまっているテストとか、外から見た振る舞いはまったく変わらないにもかかわらず、実装の中身のところをちょっと変えたら、テストがなぜかバンバン失敗し始める。テストと実装の構造的結合度が高すぎるんですね。不必要に詳細までテストしてしまっていると、こういうことが起こってしまいますと。これもなるべく減らしていきたい。

偽陰性の典型的パターン

じゃあ、偽陰性のほう。「火が出ているのに火災報知器が鳴らない」のパターンも、いくつかあります。一番ベタなやつは、空振りです。テストを動かしていると思ったらスキップしていましたみたいなパターンですね。現場レベルでは「このテストをスキップする」みたいなマークとかアノテーションみたいなのが、けっこうあります。「@Disable アノテーション」とか「skip」っていうメソッドとか。

それを開発中につけたままで、そのままコミットしてしまって、動かしてオールグリーンだと思っていたらそもそも動いてなかった、みたいなのが一番ベタだけど実は現場でよくある偽陰性のパターンです。

2つ目は、カバレッジ不足とテスト不足。カバレッジ不足っていうのは、書かれるべきテストが書かれていない。コードカバレッジを見てみるとこの行を通ってないな、みたいなところ。テストをすべき行がテスト通ってないのであれば、その行にバグがあってもテストは成功してしまいますね。そこを通るテストがないからですね。

これはコードカバレッジツールを通せば、可視化はできるので倒すことはできます。手ごわいのはどっちかというとテスト不足のほうで、これは、書かれるべきコードが書かれていない場合です。書かれるべきプロダクトコードが書かれていない。

つまり人間の仕様レベルで把握できていない時には、コードが書かれていないからテストは通っちゃうんですね、当たり前だけど。書かれるべきコードがないから書かれるべきテストもないです。人間が把握できていない。こいつがラスボスです。

もう1つ、自作自演っていうやつがあって、テストとコードが、自作自演の関係になっちゃっているっていうもので、よくあるのはこれですね。画面を2つに2分割して、上がテスト対象のプロダクトコード、下がテストコードだと思ってください。このテストは、成功するんですね。成功するんだけどプロダクトコードにはバグがあります。

このサンプルコードは、JavaScriptです。これは商品の価格の中から税額だけを返すメソッドだと思ってください。JavaScriptのNumber型は浮動小数点数型なので、端数が出ます。1円未満の端数が出るバグがある。けど、このテストは成功しちゃう。なんで? プロダクトコードのほうの期待値も同じ計算式で計算しているからなんですよね。

これはですね、現場に行くとめちゃくちゃよく目撃します。これと似たようなかたちの、結局プロダクトコードで望む振る舞いをテストコードのほうで再現しちゃっているから、テストに実質なっていないっていうのは、これだけじゃなくても、めちゃくちゃよくあるんですよ。

例えばモックオブジェクトで、「ここでデータベースで何をやったらこれを返します」「そのとおりです」「はい、そうです」みたいな感じの自作自演のテストが、めちゃくちゃよくありますよね。よくよく考えたら、なんか自分でやって自分で何か答えてみたいなところのテストになってしまっているみたいなのも、とてもとてもよくあるので注意しましょうという感じです。

(次回につづく)

続きを読むには会員登録
(無料)が必要です。

会員登録していただくと、すべての記事が制限なく閲覧でき、
著者フォローや記事の保存機能など、便利な機能がご利用いただけます。

無料会員登録

会員の方はこちら

関連タグ:

この記事のスピーカー

同じログの記事

コミュニティ情報

Brand Topics

Brand Topics

  • 1年足らずでエンジニアの生産性が10%改善した、AIツールの全社導入 27年間右肩上がりのサイバーエージェントが成長し続ける秘訣

人気の記事

新着イベント

ログミーBusinessに
記事掲載しませんか?

イベント・インタビュー・対談 etc.

“編集しない編集”で、
スピーカーの「意図をそのまま」お届け!