2024.12.19
システムの穴を運用でカバーしようとしてミス多発… バグが大量発生、決算が合わない状態から業務効率化を実現するまで
リンクをコピー
記事をブックマーク
和田卓人氏:じゃあ次。テストダブルの話にいきます。「忠実性と決定性のトレードオフを理解しよう」という点です。これはもうちょっとあとにまた出てきます。
テストダブルというもので、モックオブジェクトとかスタブとかを使って、本物ではない偽物をテスト用に使ってテストをすることはよくありますよね。
データベースの偽物とか外部システムの偽物とか、AmazonのS3の偽物とか。本物を使えない、あるいは使いたくないからテスト用の偽物で置き換えるということをよくやります。
こういう、テスト用に使う偽物のことを総称して「テストダブル」と呼びます。テスト用の偽物を何と呼ぶかは混乱しきっていた歴史があります。ある人はこういうテスト用の偽物のことを「モック」と呼んで、ある人は「フェイク」と呼んで、ある人は別の呼び方をしてみたいな感じになっちゃうんですね。ある人が「スタブ」と言っているものを他の人は別の言葉で指しているとか、そういうのがいっぱいあるんですよね。
混乱しきっていたところを1つの本が大統一しました。それが『xUnit Test Patterns』という本です。ものすごく分厚い本なんですよね。(スライドを示して)この本が『xUnit Test Patterns』です。
ジェラルド・メーサーロシュという著者の方が、その混乱しきっているテスト用の偽物のことを整理整頓しました。テストに使う偽物のことを総称して「テストダブル」と呼びます、と。
テストダブルの詳細は、ダミーとかスタブとか、スパイとかモックとか、フェイクとかになります。それぞれがどういう意味なのかとか、どういう役割は時間の関係で深入りできないんですが、我々がよく「モックに置き換えよう」と言っている時、そのモックは狭い意味でのモックと、広い意味でのモックがあると思ってください。
広い意味でのモック、テスト用の偽物のことを総称して、我々はよく「モック」と呼びがちですが、より細かくより正確にはテストダブルのことです。
つまり、広義で総称したテストダブルのことをモックと呼んだり、あるいはテストダブルのサブ概念のことをモックと呼んだりしているので、ある人が「モックオブジェクト」と言った際にどちらを指しているのかは、まだ混乱しているような感じだったりするんですね。
テストダブルの利点でまず1つ間違いなく良いのは、テストしにくいものをテスト可能にすることです。例えばネットワークエラー。本物のネットワークでネットワークエラーを起こしてテストするのは不可能ではないんですが、だいぶ面倒くさいところがある。ディスクフルの場合の振る舞いとか、そういった例外系のテストにテストダブルはすごく強みを発揮します。
あとテストダブルは本物よりも速度が速い。メモリ上でエミュレーションしたりするので速度が速いことが多く、かつ不安定さも減ります。決定性とは毎回同じように動くということです。なので、速度と決定性が上がります。
その代わりモック、テストダブルにはデメリットもあります。まず1つはテストが脆くなることですね。モックオブジェクトとかテストダブルを使い過ぎると、実装の内部に深く根差したテストを書いてしまいがちです。
そうすると外部から見た振る舞いは変わっていないにもかかわらず、ささいなところですぐに失敗するようなテスト。細か過ぎる、詳し過ぎるテストになってしまいがちです。これが脆いテストを招いてしまう、偽陽性を招いてしまう。
もう1つはテストの偽陰性を招くということもありますね。よくよく見てみると、自作自演になっちゃっているテストってすごくあるんですよ。
データベースを偽物に置き換えているとします。データベースを偽物に置き換えた上でデータベースアクセスするテストを書いていると、「実はデータベースにアクセスしたらこうなっていること」みたいなところが周り回って、期待値とかも全部モックのほうで用意していて。
「自分がこう動くべき」と思った振る舞いをモックにさせて、そのとおりに動いているようなテストになっちゃっていて実はなにもテストになっていないというのもけっこうあるので、これもすごく注意してください。
次にテストピラミッドです。今日の講演のテーマもそうですが、私の講演のテーマは自動テストをどれだけ中長期的に信頼できる状態にしていくかということです。
なので、信頼できるテスト、(つまり)成功も失敗も信頼できる。成功していればリリースできるし、失敗していればコードを直さなきゃいけない、かつ、なるべく早く信頼できる結果に到達するようなテスト群を作りたいんですね。
ということで、そういったテストのモデルは、よく「テストピラミッド」と呼ばれています。どのくらいのところにどのくらいテストを書いたほうがいいのか。テストケースの数の理想的な比率を一種のピラミッド型で面積を示した絵みたいなものなんですね。テストピラミッドでは、ユニットテストが全体を分厚く支えているべきであるという考え方があります。
なぜならユニットテストは実行のコストとか記述のコストが低くて速度が非常に速い。それで同じように毎回動いてくれる。そのユニットテストの上にインテグレーションテストが乗っていて、その上にエンドツーエンドテストが乗っている。エンドツーエンドテストは実行速度が遅く決定性が低く、動作が不安定、それでコストが高い。その代わり忠実性が高い。忠実性というのは本物らしさ(のこと)ですね。
本番環境の本物の動きを模している度合い自体は、エンドツーエンドテストはテストに本物を使うので忠実性が高いです。ユニットテストはテストに偽物、テストダブルとかを使うことが多いので、忠実性は下がっていく。つまり自作自演のリスクをはらんでいるということになるわけですね。
テストピラミッドと対照的なアイスクリームコーンアンチパターンがあって、理想的なのはテストピラミッド型ですが、現場でよくあるのがアイスクリームコーン型。つまり、手と目でたくさんテストをしていて、エンドツーエンドテストとかGUIレベルでのテストが多くてユニットテストはぜんぜん少ないという、アイスクリームコーン型のアンチパターンに陥ってしまう現場がすごく多いです。
最近は議論もいろいろあって、「ピラミッド型は古いんじゃないか」とか「トロフィーモデルが良い」とか「ハニカムモデルが良い」とか、いろいろ言われるんですけれど。でもこの議論のブレをよくよく見てみると、ユニットテストとは何かとか、インテグレーションテストとは何かというところの、先ほど言っていた解釈のブレがそのままモデルの議論のブレにつながっちゃっていることが多い。
実は一貫した分類基準に照らしてこれらのモデルを再び見てみると、ピラミッド型であることが多かったりするんですよね。
(そうなると)「その一貫した基準って何だっけ? ブレがないテストの分類基準ってそういえばさっき話してきたよね」という話になるんですよね。テストピラミッドはよくE2E(End to End)、インテグレーション、ユニットテストといったテストスコープで各段が定義されることが多いんですが、我々はよりブレの少ない一貫した分類基準を議論してきました。
つまり、テストサイズでピラミッドを組むほうがよりブレの少ない議論ができて、テストをどういった比率で運用していくかというところを、よりブレのないかたちで議論できるようになるんですね。ユニットテストが全体を支えているべきであるというより、スモールテストが全体を支える。
スモールテストの中にはユニットじゃないやつもあったりしますよね。インテグレーションテストでもスモールテストで動かせるものもあったりする。そういう比率の中で、スモールであればあるほど速度が速く安定しています。ラージであればあるほど速度は遅くて不安定なんだけど、その代わり本番を模しているというトレードオフのモデルができます。
さあ、最後です。でも多くの現場ではアイスクリームコーンから始まります。なぜなら自動テストが書けていないとか、書きにくいところからだんだん……。今は手動テストでカバーしていたところを、例えばAutifyを使ったりエンドツーエンドテストを自分で書いたりして、自動テストがそもそも書きにくいところに対して外から包んでだんだん自動化の度合いを高めていくことをするので、最初のテストの比率としてはアイスクリームコーン型から始まる。
特にレガシーシステムとか、大規模システムとかを抱えていると、アイスクリームコーン型の比率から始まることはとてもたくさんあります。最初はだんだん外から自動化していくのは悪いことではありません。
ということで、最後は自動テストのサイズダウン戦略ということで、サッと話します。
ただ、アイスクリームコーンのかたちだと長続きしないんですよ。ラージテストが多過ぎると実行結果が不安定になっちゃって、偽陽性が増えてしまって信頼性が損なわれてしまう。結果的にテストの失敗に対してそれが日常になって鈍感になっちゃう。だからアイスクリームコーンをどうやってなるべく早いうちにピラミッドにしていくかが大事なんですね。
ということで、まずラージテストをミディアムテストにどうやって移植していくかと、これはテストダブルの仲間の中で一番地味ですが、フェイクオブジェクトというものを使います。フェイクオブジェクトは何かというと、テスト用の代替実装のことです。
具体例を挙げるとわかりやすくて。例えばAmazonのDynamoDBを使っているとします。DynamoDB Localという、AmazonのAWSが推奨しているテスト用のエミュレータがあったりします。このDynamoDB LocalはDockerコンテナ上で動いたりするんですね。本物のDynamoDBを使ってアクセスするのは、外部ネットワークにアクセスしにいくから、当然ラージテストです。なんですが、DynamoDB LocalをDockerコンテナで立ち上げてCIのマシン上で動かせば、サイズはミディアムサイズに収まりますよね。
つまり、DynamoDBを使った本番のコードであっても、ラージテストしかできなかったものが、フェイクオブジェクトを使えば、つまりテスト用の別実装を使えば……。例えばDynamoDBに対するフェイクオブジェクトがDynamoDB Localです。この実装を使えばミディアムサイズに収まるとか。LocalStackというOSSのフェイクの集合もあったりするので、この実装を使えばラージからミディアムに持っていける。
じゃあミディアムからスモールにどうやっていくかというと、これはテストをしたいところとテストがしにくいところを分離することによって分けることができます。テストしたいところがスモールテストしにくいとか、そもそもテストしにくいこともよくあるんですよね。
(スライドを示して)そういった時に、例えばこの歯車みたいなやつがテストしたいロジックだと思ってください。テストしたいロジックがあるけれど、それがテストしにくい入れ物、例えば外部環境上で動いてしまっているとか、あるいはUIに密結合しちゃっている。だからサイズが上がらざるを得ないみたいなものもよくあったりするんですよね。
これの細かい説明はさすがにもうする時間はないんですが、テストがしにくい要素を細かく薄く切り取って、テストしたいところを中で抽出して、例えば関数に抽出したりクラスに抽出したりすると、インメモリでテストが書けるようになりますよね。スモールテストが書けるようになる。
この緑の丸ポチが付いたテストしにくいところは、ミディアムテストとかラージテストをせざるを得ないんだけど、テストしたい大部分はスモールテストが書けるかたちでできます。
そうすることによって、アイスクリームコーン型からピラミッドに移植ができるわけです。
ということで、これまでの知見を1枚絵にまとめてみると、こうなります。テスト全体の信頼性を維持するためにはどうするかというと、ブレがない基準でピラミッドを作りましょう。各テストサイズでピラミッド型を配置します。サイズダウンはどうやるかというと、テストダブルでします。ラージからミディアムへテストダブルでサイズダウンします。
ミディアムからスモールへはテストダブルを使ってもいいけれど、先ほどのHumble Objectパターンみたいなテストが容易になるような設計とか、あるいはドメイン層の抽出とか、リファクタリングとか、そういったテクニックを使ってテストしたいところをスモールにどんどん切り出していくかたちになります。これがテスト全体の信頼性を維持するモデルです。
ごめんなさい。すごく時間がオーバーしている。これで終わりです。
最近良い本が出過ぎて、私の仕事を脅かすようなライバルが出てきています。例えば『単体テストの考え方/使い方』という本があります。この本は内容が良過ぎて、私がいろいろ説明したいことがほとんどこの本に書いてあるような事態になっています。とても良い本なのでおすすめです。私の活躍できる領域を明らかに蝕んできているぐらい、良い本です。
もう1つ、いかにも私の言いそうなことを言うAIが出てきました。「実行順序に依存するユニットテストについて、t_wadaさんなら何と言うでしょうか?」というと、いかにも私が言いそうなことをサラッと答えるようなAIが出てきているんですよね。実際にこれは私の意見と一致します。エキスパートに聞かなくても聞けるような時代がやってきているかもしれない。エキスパートにとっては脅威を感じるような時代にもなってきておもしろいですね。これはおもしろい時代です。
最後に悲しいお知らせがあります。このようなコラムを連載してきた『WEB+DB PRESS』という雑誌ですが、残念ながら休刊になることが決まりまして、次回(vol.136)が最終回です。最終回をこれから書くところです。
いったんきちんとしたかたちで締めようと思うので、ぜひ読んでみてください。
ということで私の講演は以上です。ご清聴ありがとうございました。
関連タグ:
2024.12.20
日本の約10倍がん患者が殺到し、病院はキャパオーバー ジャパンハートが描く医療の未来と、カンボジアに新病院を作る理由
2024.12.19
12万通りの「資格の組み合わせ」の中で厳選された60の項目 532の資格を持つ林雄次氏の新刊『資格のかけ算』の見所
2024.12.16
32歳で成績最下位から1年でトップ営業になれた理由 売るテクニックよりも大事な「あり方」
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
PR | 2024.12.20
モンスター化したExcelが、ある日突然崩壊 昭和のガス工事会社を生まれ変わらせた、起死回生のノーコード活用術
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2024.12.18
「社長以外みんな儲かる給与設計」にした理由 経営者たちが語る、優秀な人材集め・会社を発展させるためのヒント
2024.12.17
面接で「後輩を指導できなさそう」と思われる人の伝え方 歳を重ねるほど重視される経験の「ノウハウ化」
2024.12.13
ファシリテーターは「しゃべらないほうがいい」理由 入山章栄氏が語る、心理的安全性の高い場を作るポイント
2024.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
Climbers Startup JAPAN EXPO 2024 - 秋 -
2024.11.20 - 2024.11.21
『主体的なキャリア形成』を考える~資格のかけ算について〜
2024.12.07 - 2024.12.07
Startup CTO of the year 2024
2024.11.19 - 2024.11.19
社員の力を引き出す経営戦略〜ひとり一人が自ら成長する組織づくり〜
2024.11.20 - 2024.11.20
「確率思考」で未来を見通す 事業を成功に導く意思決定 ~エビデンス・ベースド・マーケティング思考の調査分析で事業に有効な予測手法とは~
2024.11.05 - 2024.11.05