「テストコードにはテストの意図を込めよう」の発表報告&補足説明&質問回答 #vstat

先日、「リーダブルなテストコードについて考えよう~VeriServe Test Automation Talk No.3~」というイベントで登壇してきました。

veriserve-event.connpass.com

今回は発表内容に対する補足と、発表に対していただいた質問に回答します。気になるところだけでも読んでもらえればと思います。

目次

発表内容

私の発表スライドはこちらです。

speakerdeck.com

他2人の発表スライドはこちらになります。

speakerdeck.com

speakerdeck.com

伊藤さんによる登壇報告の記事はこちら

blog.jnito.com

申込者数は驚異の1000人超え!*1

当日も多くの方に来ていただき、これぐらいの人数の前で発表するのが久々すぎて緊張しました。

そんな中、私が発表して思った感想はこちら

特に、私の発表を聞いて、こういう風に思ってくださった方がいたのは嬉しかったです。

発表に対する補足

発表に対しての補足が2点あります。

【補足1】都道府県のテストについて

こちらのスライドについての補足です。

発表資料15ページ

イベント当日には口頭で補足しながら発表したのですが、このスライドの前提として、どの都道府県がどの地方に属しているかのテストは別にあるとしています。

今回、このような前提にしているのは、あくまでも開発者が話している「地方ごとに送料が変わるのを確認」を表現することを目的としています。

もちろん、送料計算を47都道府県分書いても構いません。ただし、例えば同様に「地方ごとに配送日数が変わるのを確認」という別のテストが必要になった場合、

@Test
public void _青森県の場合送料が1000円 {

}

@Test
public void _広島県の場合送料が1000円 {

}

...(同様に残り45都道府県分行う)

というテストに加え、

@Test
public void _青森県の場合配送日数が2日 {

}

@Test
public void _広島県の場合配送日数が3日 {

}

...(同様に残り45都道府県分行う)

というテストも行うことになります。(47+47=94通りのテストが必要)

実際にテストコードを作成する場合は、「都道府県と地方の紐付けに関するテスト」と「地方と送料の紐付けに関するテスト」と「地方と配送日数の紐付けに関するテスト」で分けて表現することになると思います。(47+7+7=61通りのテストが必要)

テストの意図が複雑に絡み合っていると、テストケース数が必要以上に多くなったり、将来テストコードを見返した時に何をテストしたいのか分かりづらくなっている可能性があるので、やはりテストの意図を改めて見つめ直してほしいかなと思います。

【補足2】Parameterized Testsへの利用について

こちらのスライドに対しての補足です。

発表資料22ページ

「このような表で整理したならばParameterized Testsにそのまま活用できるのではないか?」という意見がありました。

私としても活用できると考えています。

しかし、その際には同じテストの意図のものだけでParameterized Testsのケースをまとめることを考えた方が良いと思います。

具体的には、表をそのまま移して以下のようなParameterized Testsを作成するのは止めた方が良いです。

@ParameterizedTest
@CsvSource({
    "'テスト太郎', 0, true, false",
    "'テスト太郎', 1, true, true",
    "'テスト太郎', 4, true, true",
    "'テスト太郎', 5, true, false",
    "'', 2, true, false",
    "'テ', 2, true, true",
    "'最大文字数テスト太郎', 2, true, true",
    "'最大文字数を超えた太郎', 2, true, false",
    "'テスト太郎', 2, true, true",
    "'テスト太郎', 2, false, true"
    })
void _予約可否のテスト(String reservedName, int numberOfReservations, boolean isPrint, boolean expected) {
    assertEquals(expected, isReserved(reservedName, numberOfReservations, isPrint));
}

元々整理していた表は、「予約人数に関するテスト」と「予約名の文字数に関するテスト」と「予約番号の印刷に関するテスト」の3つが合わさった表現されています。

そのままParameterized Testsに持っていってしまうと、実施しているテストケースは、これらの3つのどれをテストしたいのか、テストの意図は何なのかが見えなくなります。

なので、Parameterized Testsに持っていこうとする場合も、テストの意図を意識してほしいなと思います。

例えば、こんな感じに一つずつテストの意図を分けた上でParameterized Testsに持っていくのは良いと思います。

@ParameterizedTest
@CsvSource({
    "0, false",
    "1, true",
    "4, true",
    "5, false"
})
void _予約人数が1名以上4名以下の場合予約できる(int numberOfReservations, boolean expected) {
    assertEquals(expected, isReserved("テスト太郎", numberOfReservations, true));
}

また、先ほど示した送料の事例の場合、仮に東北地方と関西地方で同じ送料500円だったとしても、東北地方と関西地方の都道府県をまとめて1つのケースにしてParameterized Testsに変換しようとするのは良くないです。

テスト設計技法の言葉で言えば、同値分割法における同値クラスが何なのか考えてみるのが大事だと思います。

いただいた質問の回答

今回は参加者が多かったこともあり、事前や当日にたくさんの質問をいただきました。そちらにも回答していきます。

【質問1】リーダブルなテストコードの勉強方法はありますか?

例えばTDDBCに参加してみるのはいかがでしょう。

devtesting.jp

このイベントではTDDの体験も重要ですが、コードレビューを貰えるのも貴重です。

私がTAとして参加している場合は、今回の発表のようにテストコードやテストメソッド名についての指摘が多めになりますので、機会があればぜひ。

【質問2】テストコードのメンテナンスをするにあたってのリファクタリングの頻度はどれくらいか?

個人的には、「さあ、リファクタリングをしよう」と思っていたら負けだと思っています。

特にUnitテストの場合、常にリファクタリングして、テスト実行してる習慣になっていることが大事かなと思います。

リファクタリングの頻度を学べる動画はいくつかありますが、TDDBC Online #1 でのt_wadaさんの基調講演と、TDDBC Sendai Xのペアプロデモは特にオススメです。

youtu.be

youtu.be

【質問3】レビューをする際、機能自体のレビューにかけた時間に対してテストのレビューにかける時間はどのくらいの割合で行っていますか?

レビューの目的によります。

例えば、私の場合はテストコードがほぼ100%に近いぐらいの割合で見ると思いますが、それはレビュアーが私だけでないため、他の人がプロダクトコードのレビューをしているから実現できているだけだと思っています。

全ての人を合わせて見たときの割合というのは計測したことが無いので分かりません。申し訳ないです。

【質問4】どれが非効率な自動テストなのかが分からない状態(自動化したテスト仕様とマッピングがない)の場合、何から管理すれば良いか。

まずは管理云々の前に、それぞれの自動テストが何をテストしたいものなのか明らかにするところからではないかと思います。

【質問5】テストファイルの構造について。1つにまとめる?複数作る場合はどんなファイル構造?

E2Eテストの場合、featureごとにまとめたいです。

ファイル構造については、『The BDD Books - Formulation』に案が載っているのでそちらも参考にしてください。現在、鋭意翻訳中です。

leanpub.com

【質問6】レガシーな巨大リポジトリにテストを追加しようと思って二の足を踏んでいるのですが、どういったファイルからテストを書いていますか?また、どうすればレガシーコードにテストを導入しやすいでしょうか?目的や優先度、テストの書き方など、考慮すべきポイントがあれば教えて下さい。

まずは、「一番外側の振る舞いをテストで抑える」やり方と「内側の中で一番リスクがありそうな部分(例えば金額計算)からテストを書く」やり方があるかと思います。具体的にはケースバイケースだと思います。

レガシーコードに対してどのように書いていくかについて詳しくは、手前味噌になりますが、技術同人誌を以前に書いたのでそちらも参考にしてください。

leanpub.com

【質問7】テストを書いた時に、テストのコード量に応じて仕様変更に対して動きが重くなると思いますが (テストを沢山直さないといけない)これは仕方がない事なのでしょうか?何か工夫はありますか?

仕方のないこと、というよりも良いことだと思います。

なぜなら、そのようなテストがない場合、成功するかもしれないテストも含めて手動でテストを実行しなければいけなかったり、テストしなかった結果リリース後に大きな問題に発展するかもしれません。

それらに対して、「失敗したと判明したテストのみを対象としてテストもしくはプロダクトコードを直す」という作業は比較的コストが安い行動であり、幸せなことかと思います。

【質問8】意図が伝わるという観点で、テストコードには積極的に日本語を含めたほうが良いのでしょうか?コメントで補足するくらいなら日本語を含めよう、みたいなポリシーはありますか?

発表中にもお伝えしましたが、難しい英語で書いて伝わりづらいのであれば、テストメソッド名は日本語にした方が良いと考えています。

あとはプロジェクトでのチーム構成によります。全員日本語話者であれば日本語で良いと思いますし、OSSのように全世界でメンテナンスされていく場合は英語で書いた方が良いと思います。

【質問9】そもそもテスト設計はどのように管理されていますか?システムもしくはExcelMarkdownなど。

現在の所属での話ではなく、今までの経験上の話になりますが、

という感じです。これらの成果物のURLをJiraのチケットに貼っておくような運用を取ることが多かったです。

おわりに

今回は「リーダブルなテストコードについて考えよう~VeriServe Test Automation Talk No.3~」のイベントの補足的なことを書きました。

この記事を見てさらに聞きたいことがありましたら、Twitterにリプライを投げてください。よろしくお願いします。

*1:しかも私はほぼ宣伝をしてない…!