「いのりん」推し

Kanon氏:Kanonと申します。今日は、「テスト品質を向上させよう!」ということで、アンチパターン回避メソッドをお話ししようかなと思います。

まず簡単に自己紹介です。もしかしたらご存じの方もいらっしゃるかと思うんですけど、虎の穴ラボという会社でお仕事をしています。最近、3次元に嫁が1人できまして、2次元にはいっぱい(嫁が)います、という感じですね、はい(笑)。

(会場笑)

本業ではあんまりPHPを書いていなくて、サーバーサイドのKotlinとNext.jsを使っていて、副業と、あと前職までの4年半ぐらいがずっとPHPで、今も触っているという感じでした。

例に漏れず、アニメ、漫画、声優ラジオが好きです。みなさん、反応してくださいね。推しは、水瀬いのりさんと早見沙織さんです。

「いのりん」の推しなので、今日はちゃんと小田原のTシャツじゃなくて、いのりんのライブTで挑んでいるという状況です。

アイスブレイクがてらなんですが、「どこから来た?」ということで、私は今日、神戸から来たんですけど。じゃあ、北海道・東北から来た人がいれば、ちょっと手を挙げていただけますと……。おぉ。じゃあ、関東の方はいらっしゃいます? やはり多いですよね。そうですよね。

じゃあ、中国・四国地方の方はいらっしゃいます? いたらすごいけど、さすがにいないか? あっ、いらっしゃいますね、ありがとうございます。じゃあ、九州・沖縄の方は、いらっしゃいますかね? いないか。あえて関西を最後に持ってきたんですけど、関西から来られた方はいらっしゃいますか? あっ、僕だけ? 悲しいな。

(会場笑)

関西を最後に持ってきたのは、ちょっと理由がありまして。少しだけ宣伝というところで、「PHP"オレ"カンファレンス」なるものを開催しようかなと思っていますので、興味のある方がいらっしゃれば、ぜひ。気軽なやつなので登壇していただけるとうれしいです。というのと、これは拡散してくださいというところでした。

「何をテストするか?」「どのような状況とシナリオか?」「期待される結果は何か?」

はい、すみません。閑話休題ということで、本題に入っていきましょう。

アンチパターン回避メソッドを、大きく3つに分けてお話ししようかなと思います。

「明日からでもすぐにできること」と、「継続的に取り組む必要があること」と、「さらにその先へ……」、ということで、もうちょっと難易度は上がっていくかなという。難易度というよりかは、大変になってくる話なんですけど、そこまでやっていきたいなというお話をしようかなと思います。

最初にちょっとお断りしておくことがあります。今日出てくる事例は、必ずしも虎の穴ラボだけでやっている事例ではないということは、あらかじめご了承ください。

テストコード自体も、「PHPUnit」に限った話ではなくなってきてしまうんですけど、「Jest」だったり「Cypress」だったり「JUnit」だったり「PHPUnit」だったり「Pest」だったりのお話を交えるんですけど。そういうところの枠を越えて普遍的に使えるお話をさせていただこうかなと思っていますので、ぜひみなさんの知見の一部になればよいかなと思います。

明日からすぐにでもできることを、トントンいこうかなと思うんですけど。まず1つですね、テスト名には3つの要点を含め、明確にするということをやりたいなということです。

「何をテストするか?」「どのような状況とシナリオか?」「期待される結果は何か?」というところをテスト名につけてほしいなということです。

で、アンチパターンを紹介するんですが。例えば入力された都道府県が存在しているかどうかをチェックするテストがあった時に、こういう「isExistPrefecture()をチェックする」みたいなテストメソッドの名前をつける人はいると思うんですけど。

いやいや、待ってくれと。「テストコードが、『これ、何を調べてんだ?』というのがさっぱりわかりません」というのと、「このテストがこけた時に、結局お前は何をチェックしとるんじゃい」という話になってきて、何が悪いのかが、ぱっと見でわからんよねというお話です。

なので、今突っ込んだことなんですけど、「チェックって、これは何をチェックするんすか?」というのと、「具体的に何を検証していて、どういうことを期待しているのか?」ということを含めてほしいなというところで、明確にしましょうという感じなんですけど。

こういう感じで、存在しない都道府県を指定した時にエラーになることを期待しているよということだったりだとか、それ以外にも文字列のチェック、必須チェックとかをする時は、「そのメソッドは、requiredというプロパティがfalseの時、空文字でもエラーにならないよ」ということを書いてほしいなというところです。

AAAパターンでテストを構成する

次は、AAAパターンでテストを構成するというやつです。これは音楽グループのことではなくて、「Arrange」「Act」「Assert」の頭文字を取ってAAAということになっています。

準備、実行、検証を分けて書こうね、というお話です。アンチパターンなんですけど、これは、「classifyCustomer()は5万円以上購入したユーザーをpremiumに分類する」というテストをやろうとしています。

前に出てきた、テスト名はちゃんとはっきりするようなかたちになっているんですけど、これは最後に検証しているらしいということはわかるんですが、どこからが準備で実行なのかが、ぱっと見でさっぱりわからんよねというお話です。さっきのAAAというやつを入れてあげると、どこまでが準備で、どこが実行で、最後に検証というのがちょっとすっきりしたかなという感じです。

パラメタライズドテストで多様な入力値を検証

次がですね、「パラメタライズドテストで多様な入力値を検証」というところです。これは、ちょっとさらっと見ていただければ大丈夫なんですけど、簡単なFizzBuzzのコードみたいなやつをテストしようとしています。

3の時と、5の時と、15という3と5の公倍数の時、それ以外の時をテストする必要があるかなというところで、アンチパターンなんですけど(笑)。「殺すぞ」というコードになっていて、パターンの数だけ馬鹿ほどテストコードが書かれていて、「DRYとは?」というようなコードになっちゃっていますよというところですね。

これを解消しましょうという感じなんですけど、入力に対して複数の結果を得られるパターンというのは、このパラメタライズドテストというやつを使うことによって、こっち側ですね。あらかじめ用意したパラメーターを複数回入れて、それが期待した結果を返すよということを見ることによって、さっきの乱雑というか、何回も同じことを書いたようなテストが解消されますよね、というところです。

エラーはキャッチせず、エラーを期待する

次、「エラーはキャッチせず、エラーを期待する」というところです。今度は、0という……FizzBuzzの関数に0が渡ってきた時に、「0はFizzBuzzの対象外です」という例外を吐くことを検証したいですよ、というようなパターンがあります。

テストコードなんですけど、これはアンチパターンの時で、なんかよくわからんことをして……これは実際にある、僕が見た例なんですけど。例外をがんばってテストコードの中でキャッチして、そのキャッチしたメッセージがこれですよ、というのを検証しようとするという。なんかちょっと、半分クレイジーなことをやっているという感じですね。

「何をしているのか、ぱっと見でわかんねぇ」というところ。テスト名とかがまだまともなので、「何をしたいんかな?」というところは、かろうじてわかりはするんですけど、もうちょっと直感的に書いてほしいですよね。というところで、もうちょっとthrowsというやつを……これはテストのパターンなんですけど、例外を吐くことを期待するかたちのテストコードに書き換えましょうという感じですね。

こう書いてあげると、ぱっと見で、throwsしたクラス、エラーの例外のクラスというのと、こういうメッセージが返ってくるよねというのが直感的になって良いのかなというところです。

ここまでが、明日からすぐできることという感じで、今度は継続的に取り組む必要がある話です。今度は、「コードカバレッジを適切に保とうよ」というお話になっていきます。

(次回につづく)