風間氏の自己紹介と、本発表で話すこと

風間裕也氏:「テストコードにはテストの意図を込めよう」と題して、ブロッコリーこと風間が発表します。まずは自己紹介です。ブロッコリーと呼ばれている風間です。(スライドを示し)右上のイラストがSNS上のアイコンです。株式会社ビズリーチのプロダクト品質管理部に所属しています。

社外活動としては、JaSST Reviewのシンポジウムの実行委員長をしていたり、1泊2日の宿泊型のテストに関するワークショップ「WACATE」の実行委員をしたりしています。

執筆活動もいくつかしています。最近翻訳したこの『The BDD Books Discovery』が今回の話に関わる部分だと思っています。ちょうどツイートもしました。Leanpubで販売しています。今日発表していた末村さんにもレビューをしてもらいました。

次に、今回話す内容についてです。先ほど自己紹介でも言ったとおり、私はQA(Quality Assurance)エンジニアです。このQAエンジニアの得意分野を活かして、テストコードの改善を促す事例をお話しします。実際にあった事例を用いた経験則の話を紹介していきます。

「自分はQAエンジニアじゃないのだけれど」「開発者なのだけれど」という人も、「こういう思考を持ってやるといいんだな」ということをぜひ参考にしてもらえればと思います。

Three Amigosの考え方

1人目に発表した伊藤さん(伊藤淳一氏)の資料の中に「まだあるリーダブルなコツ」でdescribeの説明を丁寧に書く話があったと思いますが、このあたりを詳しく、私なりの経験上の話をしていきたいと思います。

QAの得意分野を考える前提として、Three Amigosの考え方もあわせて紹介したいと思います。Three Amigosとは、3つの立場の人が集まって協調的に要件を確認する行為のことです。

3つの立場とは、QA・テスター・テスト担当者、開発者、PO・ビジネス担当者を指します。この3つの立場の人は、得意分野がそれぞれ異なると思っています。

POやビジネス担当者は、新たに作りたいFeatureがあった時に「それで実現したいことは何か」に注目するのが得意だと思います。

開発者は「今回のFeatureはどのようにすれば実現できるのか」という、Howに注目することが得意だと思っています。

それに対してQAは、Featureをこう作ると考えた時に、「じゃあ何を確認すればこれが完成した、できたと判断できるのか」に注目するのが得意だと思っています。

(スライドを示し)ただ、下にも書いたとおり、これはあくまでも得意分野なので、別に責任分担をしているわけではありません。「QAはこういうところが得意だと思っています」ということです。

本発表では、この得意分野である「何を確認すればいいか」に注目して実施した会話を3つ紹介していきたいと思います。

「わかりやすいテストメソッド名を考える」事例

1つ目の事例です。「わかりやすいテストメソッド名を考える」です。(スライドを示し)都道府県についての項目です。

ここについてのテストをする時に、例えば「Test 青森県の場合、広島県の場合」と書いたとします。とりあえず、これでテストで書いて通ったものとします。その時のQAと開発者の会話を持ってきました。実際にあった例です。

「今回、青森県の場合のテスト、広島県の場合のテストをしたことは、これでいったい何を確認したかたったのですか?」と尋ねたところ、開発者は「実は、選んだ都道府県によって送料が変わることを確認したかったのです」と答えました。

「じゃあそれをテストのメソッド名にぜひ入れてみましょう」ということで、例えば青森県の場合の送料が1,000円、広島県の場合の送料が510円、と入れてみました。

「これには青森県の場合、広島県の場合と書いてありますけど、これは47都道府県すべてでテストをしたほうがいいですか? 残り45個、全部やったほうがいいですか?」と聞いたところ、「いや、ここでは地方ごとに送料が変わるのが確認できればOKです。青森県が東北地方であることは別のところでテストしているので大丈夫です」という返答がありました。

「ということは、青森県の場合、広島県の場合と書くより、例えば、東北地方の場合の送料が1,000円と書いてあげて、具体的に青森県を使ってみる、と書くといいのではないか」と改善していきました。

この時の東北地方の場合は、ハイレベルテストケースと呼ばれます。要は抽象的なテストケースです。具体的な値はローレベルテストケースと呼ばれます。

ローレベルなものをテストメソッド名に書いてしまうと、一見それをやっているから意図が伝わるように思えて、実際は伝わらないことがあります。したがって、ハイレベルテストケース、ローレベルテストケースを意識することが大切だと思っています。

なぜこのようにするかというと、もし青森県出身の人がいたら申し訳ないですが、もしも将来、万が一青森県が統廃合で存在しなくなった場合に、どのようにテストコードを変えればいいかが一目でわかるからです。

東北地方に他にどのような県があるかがわかっていればすぐに選べます。先ほどのローレベルテストケースの場合だと、青森県がなくなった場合に「じゃあこのテストはどう変えればいいのだろう?」もしくは「このテストを消せばいいのかな?」という判断が難しくなります。このようにすることによって、「今回は東北地方の場合を調べたいんだ」という意図が伝わるから、メンテナンスもしやすくなります。

(スライドを示し)実は、伊藤さんも今回の発表の中で区別して書いています。誕生日の話やageの話をしていましたが、ここに「45歳が返る」などとは別に書いていないです。「誕生日を過ぎた年齢が返る」と書いていて、今回の場合は具体的に45歳を持ってくるこ(ことになる)。区別して使いわけているということを、発表資料を事前に見て思ったところです。

「テストの意図をテストメソッド名に反映する」事例

続いて2つ目の事例です。1つ目もそうと言えばそうなのですが、「テストの意図をテストメソッド名に反映する」。題材は少し変えていますが、似たようなことを実は社内でもやっていたというものです。

(スライドを示し)この画像は座席順番待ちシステムの入力画面です。飲食店で待ちが発生した時に、受付をしておくシステムだと思ってください。4名まで予約できて、氏名は10文字まで入力可能で、予約番号を印刷するかどうか、順番待ちの登録ができるかどうかのテストを考えるというものがあったとします。

このテストを実際に書いてみました。開発者が書いたところ、このようになりました。1つ目のテスト。氏名がテスト太郎で人数0名、予約番号を印刷する。2つ目のテスト。氏名がテスト太郎で人数1名、予約番号を印刷する。3つ目のテスト。氏名がテスト太郎で人数2名、予約番号を印刷しない。

テストしたい気持ちはわかります。ただ、これで何をテストしたいのかがちょっといまいちピンときませんでした。

なので、「今回のテストでは何を気にしているのですか? ちょっとコメント行に書いてある情報量が多くて、何をテストしたいのかよくわかりません」と言ったら、「自分も書いていて多いと思っていました。実は、コードを書く前に内容をすでに整理していて」と、こういう表をチラッと出してくれたのです。

テスト設計書を書いていたものが後から出てきて、「これはいいじゃないですか。これをテストコードにちゃんと意図がわかるように書きましょう」という話をしました。

今回は人数が0名の場合は予約できない、1名の場合は予約できる、印刷しない場合は予約できる、という3つをテストコードで表そうとしていたことが(話を)聞いてわかりました。

つまり、いろいろ書いてあった中で本当に確認したい入力値は、スライドの赤字で示した1つの部分だけでした。人数が0名の場合と1名の場合。(スライドを示して)ここに2名と書いてありますが、こちらよりも印刷しないことを確認したくて、たまたま2名を選んだわけです。

本当に確認したい入力値ではないが、テストコードを実装する時に必要となってしまった入力値が、スライドの緑字で示した部分です。したがって、本当に確認したい入力値(一つ前の画像の赤字部分)をぜひテストケースに書きましょうということです。

(スライドを示して)例えばこのような感じです。人数が0名の場合は予約できない、1名の場合は予約できる、予約番号を印刷しない場合は予約できる。氏名のテスト太郎は、実際のテストメソッドの中身で具体的に書いてあげればいいと思います。

これはJavaを書いていますが、JUnit5だとNestedというアノテーションがあります。それを用いると、テストの意図を整理することも可能だと思っています。

例えば、0名や1名というのは、予約できる人数が1名以上4名以下ということを調べたくて、そのテストを書いていたのです。これもハイレベルテストケース、ローレベルテストケースの1つの例かもしれませんが、このように書いてあげるとなおテストの意図が整理しやすいと思っています。

「会話を通じてテストの意図を見つけ出す」事例

最後に3つ目の事例です。「会話を通じてテストの意図を見つけ出す」を紹介していきます。題材は自動販売機です。実際に使ったのがCucumberとJUnitで、CucumberのGherkin記法を使った話です。与えられた要求は次の3つです。100円硬貨を入れたら入金額を100円に、100円硬貨を入れた後に50円硬貨を入れたら150円に、1円硬貨には対応しないです。

(スライドを示し)そのお題をもとに作成した結果がこちらです。スペースの都合上、JUnitは割愛します。シナリオがあり、途中でJUnitを挟んで実装コードがあります。実装コードもいろいろとリファクタリングしたい部分もありますが、今回はテストシナリオに注目します。

このテストシナリオをさらに改善していくことを考えます。実際にQAと開発者、POもいましたが、主にQAと開発者の会話を今から載せていきます。

まずQAが「ここでやろうとしていることはなんとなくわかりました。しかし、シナリオ名がすべて『入金額確認』となっていて、同じ名前だと何をテストしたいのかがわかりにくいので、変えたほうがいい気がします」と言いました。

「そうしたらこんな感じですか」ということで、テストシナリオ名を1回投入時の入金額、2回投入時の入金額、使用不可の硬貨投入、と変えました。

さらに、「ちなみに、この中の2つ目のテストの意図は何ですか?」と聞いたところ、開発者が「コインを1回だけではなくて2回投入した時もちゃんと動くか確認したいという意図があります」と答えました。

「そうしたら、2回投入時と書いてありますが、1回投入時、2回投入時と書いたら、3回投入時もやりたくなってしまう気がするのですが、3回目はどうなるのですか?」と聞いたら、「3回目は2回目と同じようにどんどん加算していく仕組みなのでロジック上は大丈夫で、わざわざテストで書かなくても大丈夫だと思います」と言われました。

「ということは、ここで注目しているのは、複数回投入した時の入金額というよりも、ちゃんとそれが加算されているかというところですね?」「確かにそうですね」となって、シナリオ名を複数回投入時の入金加算額の確認に変えました。

どうでしょう。これをそのままメンテナンスしていった時に、数年後にテストの意図がよりわかるのはどちらでしょうか。

テストプロセスを意識して作ることでメンテナンス性を高められる

「おわりに」です。今回は3つの事例を紹介し、テストメソッド名に着目してテストの意図が伝わる改善例をお伝えしました。実際にはテストの意図を確認した結果、そもそも実装しているテストがよくないかたちになっている可能性もあると思っています。その時は、いろいろなテストメソッド内のリファクタリングをしたり、もしくはテスト設計の作り直しをしたりするかもしれません。

今回は作り終わったものを会話から改善していく例をお伝えしましたが、そもそも初めからテストプロセスを意識して作ることで、さらにメンテナンス性を高めることもできると思っています。

(スライドを示し)これはJSTQB(Japan Software Testing Qualifications Board)のシラバスから持ってきましたが、テストプロセスにはテスト分析・テスト設計・テスト実装・テスト実行とあって、特に自動テストを書こうと思うと、テスト実装に注力しがちになります。「テスト実行の前にやることはテストを実装することでしょ?」「テストスクリプトを作成することでしょ?」となりがちです。

それだけじゃなくて、それ以前にやることがいくつかあると思っています。何をテストするのか、何を決定するのか、それをどのようにテストするのかを決定する、という(3つの)ものがあると思っています。特に、テスト分析からちゃんとテストの意図を込めて考えることが大事です。

実はこの話は、t_wadaさん(和田卓人氏)のTDD(Test Driven Development)のサイクルの中にもあります。(スライドを示し)Red、Green、Refactoringという言葉が有名ですが、実はt_wadaさんのこのスライドの1番。次の目標を考えるというところ。これがまさに何をテストしたいのかを考えているところなのです。

Red、Green、Refactoringの話は、このサイクルだけ聞いてしまうと、「よし、次のテスト実装をしよう」といきなりなってしまいがちですが、何をテストするのかを考えることが大事だと思っています。

「何を確認したいテストなのか」を考えることが大切

まとめです。何度も言います。「何を確認したいテストなのか」を考えることが大切です。会話を通じてテストの意図を見つけ出すことが可能です。今回は、実際にその事例を3つ紹介しました。その時にハイレベルテストケースとローレベルテストケースを意識してメソッド名を考えると、よりメンテナンス性の高いものができると思っています。

今回は作り終わった後に改善する話をしましたが、もともとのテストプロセスを活用して、初めからメンテナンス性の高いテストを作成することも可能です。途中にあった表のような感じでテスト設計をして整理しておくことで、テストの意図も整理することが可能だと思っています。

以上です。ご清聴ありがとうございました。