テストを書こう
テストは必要だと思ってるけどどうすればいいのかわからない人のためのあれそれです。
大事なこと
- ユニットテストですべてを確認しようとしない。
- ユニットテストで確認できるのは関数が規定した動作を行うかどうかだけです。
- システムとして正しいかどうかはユニットテストだけでは確認できません。
対象を決める
パッケージやクラス、モジュールの単位でテストする対象を決めましょう。
新しく作った機能、修正があった機能が基本はターゲットになると思います。
慣れないうちは入力と出力がわかりやすい関数を選ぶと良いでしょう。
ツールを用意する
世に存在するすべてのプログラミング言語にはテストツールが作られています。
テストツールには様々な機能がありますが、
共通して テストケースを作成し、テストケース毎の検証結果を出力する機能があります。
テストケースは 条件を設定して関数を呼び出し、結果を想定している結果と比較するというセットをいいます。
面倒なものはモックにする
データベースやsmtp等、用意すると面倒くさいもののインターフェースを叩く実装のテストはモックを活用しましょう。
大体のテストツールにはモックアップの仕組みが用意されています。
大雑把に、 内部で呼ばれている関数を別の関数に置き換えることができます。
データベースに接続するのであればDBに対するSQLがあっていれば問題が有りません。
そのSQLがシステムを正しく動作させるかは次の段階で行うことです。
基本的に外部のインフラを使うようなものはモックアップすると楽です。
テストケースの作り方
プログラムは入力・状態・出力を持ちます。
入力は引数です。
状態は環境変数等、入力によって変化しうるパラメータです。
出力は入力と状態によって決まる返り値です。
この3つが適切に把握できていればテストコードは簡単に書けます。
入力を決める
対象の条件分岐を網羅する入力を考える
まず、switch、ifなどの条件分岐を探します。
すべての分岐をチェックできるよう入力を上げていきます。
下記のようなコードの場合、xが4以下,5以上の場合をそれぞれ抽出します。
if(x<5){}
enumを文字列に変換するような実装の場合、面倒でもすべてのenumをパターンとして抽出したほうが良いです。
よくバグが発生しますし、目視での抽出が難しいです。
条件分岐の境界を確認する
文字数によるバリデーションだったり、数字のmin/max等の条件がある場合、min,min-1やmax,max+1などのルートが変わる境界の確認を行いましょう。
境界値は>= や> などの取り違えが非常に発生しやすいです。
# x=4,5を抽出する
if(x<5){}
# y=1,2を抽出する
if(y>=2){}
状態を決める
アプリケーションの状態を決める
in-memory cacheや呼び出し回数の保持などアプリケーションの内部状態で処理が変わる部分があるのであれば、状態を抽出しておきます。
状態を変更する関数をテスト対象実行前に呼び出すであったり、テストコードから無理やり書き換えたりなどを行って状態を作り込んでいきます。
モックのステータスを決める
DBがエラーを起こした場合などのパターンもチェックする必要があります。
モックの機能で返り値の編集や例外を発生させるなどができますので必要なモックの状態を作り込みましょう。
APIの404(NotFound)や409(Conflict)等、外部とのやり取りで処理が変わる部分を網羅していきます。
確認観点を決める
入力に対する出力をチェックする
関数の返り値を想定した値と比較します。
テストツールでassertってあったらだいたいこの比較を行っています。
アプリケーションの状態の変化をチェックする
試験対象の関数がアプリケーションの状態を変更するものであることがあります。
その場合、関数実行後のアプリケーションの状態が想定されたものと一致するかを比較しましょう。
モックの呼び出し状況をチェックする
モックツールは呼び出された回数や呼び出されたパラメータを保持しています。
これらのパラメータを確認するようにしましょう。
テストコードを活用しよう
機能改修を行う際に、先に想定する動作のテストを書いてから、実装を修正していくやり方を行ったり(=テスト駆動開発)
改修によって変更したところ以外が動かなくなっていないかを確認したりします。