Laravelでテストコードを導入する
はじめに
本記事では、Laravelを使っているプロジェクトにテストコードの導入を検討した時に「何が必要なのか」、具体的には
- テストコードはどのようなケースで導入するべきなのか
- テストコードを導入するために必要なこと
- テストコードの運用
の3点をお伝えしたいと思います。
テストコードを書いたことがない方にとって、テストコードはハードルが高いものかもしれません。 しかし、仕様変更の影響を簡単に把握できることや、保守運用を続けていくと工数の削減ができるなど、テストコードを導入することで多くの恩恵を得ることができます。
数あるバリデーションを、手動でテストする時と、テストコードでテストする時を想像してみてください。 きっと、「テストコードよりもすべて手動でテストをしたい」とはならないでしょう。
目次
- テストコードはどのようなケースで導入するべきなのか
- テストコードを導入するために必要なこと
- テストコードの運用
- さいごに
テストコードはどのようなケースで導入するべきなのか
テストは全てテストコードで自動化するべきなのか
品質を担保するためにバグを見つけ出す作業であるテストのやり方には、手動で行う方法とテストコードを使い自動で行う方法があり、どちらの方法でテストをするべきかはテストの対象によって変わります。 そのため、それぞれの特徴を理解して、使い分けることが大切です。
手動テストと自動テストの特徴
手動テストとはマウスクリックやテキスト入力、実施結果の可否などのテストの操作を人の手で行うテストです。大抵の場合は、テストの手順、条件、期待する結果などを記した「テスト仕様書」を作成し、その仕様書を元に一つ一つ手作業でテストを実施して、確認します。
手動テストの特徴は以下の通りです。
- 画面操作で行うため、レイアウトを確認しながらテストすることができる
- 想定外の不具合を発見することができる
- 自動テストでは想定するテストケースに対してテストするので、想定外の不具合を発見できません
対して、自動テストとはテストコードで実行するテストです。自動テストの特徴は以下の通りです。
- テストのオペレーションが迅速で正確
- 誤操作・誤認識などのヒューマンエラーの可能性が極めて低い
- 繰り返し行うテストに対してとても有効
- 仕様変更の影響を把握しやすい
- 仕様変更の箇所以外の機能に影響を与えていないか、つまりデグレーションが起きていないかを把握することができる
では、それぞれのテストが向いているケースは、具体的にどのようなケースなのでしょうか。
それぞれのテストが向いているケース
手動テストでテストをするべきケースは、以下になります。
- UIテスト
- コードの改修が今後も起こりそうにないところ(繰り返しテストする必要ないところ)
- 業務フローのシナリオテスト
そして、自動テストでするべきテストケースは
- 数字や文字のチェックなどの目視ではヒューマンエラーが起きる可能性があるもの
- テストパターンが多く、手動では工数がかかる項目
- 繰り返しテストする必要がある場合
- アカウント作成時の確認メール送信に関するテストなど
ただし、自動化する対象がどの程度繰り返し実行されるテストか、テスト自動化にどのような効果を求めるかによって、最終的には費用対効果を総合的に判断する必要があります。
テストコードを導入するために必要なこと
次に実際にテストコードを導入するために必要なことをUnitテストを中心に解説します。テストコードを導入するためには、以下の3つのことを最低限抑える必要があります。
- Featureテスト・Unitテスト
- PHPUnit
- テストコマンド
1つずつ紹介していきます。
Featureテスト・Unitテスト
公式ドキュメント( https://readouble.com/laravel/8.x/ja/testing.html )によると、それぞれは以下のように記述されています。
- Featureテスト
- 複数のオブジェクトが相互作用する方法や、JSONエンドポイントへの完全なHTTPリクエストなど、コードの広い部分をテスト。
- Unitテスト
- コードの非常に小さな孤立した部分に焦点を当てたテスト。Laravelアプリケーションを起動しないため、アプリケーションのデータベースやその他のフレームワークサービスにアクセスできない。
このようにFeatureテストとUnitテストの基本的な違いは「何をテストするか」、つまりテストの対象にあります。そして、テストの対象はそれぞれ
- Featureテスト
- Controller関連のテスト
- Unitテスト
- クラスのメソッドやヘルパ関数などのテスト
という認識で良いと言えます。そのほかには、UnitテストはFeatureテストと比べて、実行時間が早いという特徴もあります。
PHPUnitについて
Laravelでは標準で「PHPUnit」というフレームワークが組み込まれているので、PHPUnitを用いてテストコードを作成します。そのため、PHPUnitへの理解は必要であり、特に
- アサーション(アサーションメソッド)とは何か
- アノテーションとは何か
を理解していることが望ましいです。以下のテストコードを例にして、アサーションとアノテーションをそれぞれ説明します。
<?php
/**
* @test
*/
public function login()
{
$response = $this->get('/login');
$response->assertStatus(200);
}
アサーションとは、あるコードが実行される時に満たされるべき条件を記述して、実行時にその条件をチェックする仕組みのことです。
例では、assertStatus関数というものを使用していて、「/login」のURLにアクセスした時に、HTTPステータス200が返ってくることを確認しています。 このように、PHPUnitやLaravelには「assert〇〇」という名前のメソッドが多数存在しており、様々なテストができるようになっています。
よく使用されるアサーションは、以下の通りです。
assertStatus | レスポンスに指定HTTPステータスコードがあることをテスト。 |
assertViewIs | 指定するビューがルートによって返されたことをテスト。 |
assertSee | 指定する文字列がレスポンスに含まれていることをテスト。ビューに想定している文字列があるかテストする時に使う。 |
assertSessionHas | セッションに指定するデータが含まれていることをテスト。 |
assertDatabaseHas | データベース内のテーブルに指定したキー/値クエリの制約に一致するレコードが含まれているかをテスト。 |
assertSoftDeleted | 指定したEloquentモデルが論理削除されたことをテスト。 |
assertEquals | 期待している値と実際の値が一致するかをテスト。フォームリクエストのテストに用いる。 |
その他のアサーションについては公式サイトなどで調べてみて下さい。
- PHPUnit
- Laravel
次にアノテーションとは、メタデータを表す特別な構文のことです。
PHPUnit では、この情報を元にして、実行時の振る舞いを設定することができます。
<?php
/**
* @test
* @group user
*/
public function login()
{
$response = $this->get('/login');
$response->assertStatus(200);
}
例えば、@groupアノテーションというものは
php artisan test --group=user
をコマンドラインで実行すると、「@group user」の記述があるテストコードのみの実行を可能にします。
よく使用するアノテーションは、以下の通りです。
@test | 通常テストメソッド名は「test」から始める必要があるが、@testアノテーションを付与することで、メソッド名を「test」から始めなくてよくなる。 |
@group | 前述の通り、付与することで実行時に特定のテストのみを行うことができる。 |
@dataProvider | テストメソッドにテストデータを提供する。フォームリクエストのテストでよく用いる。 |
その他のアノテーションについても、公式サイトから調べてみてください。
テストの実行コマンド
Laravelでテストを実行するときのコマンドは以下の2種類です。
- vendor/bin/phpunit
- PHPUnitの標準的なコマンド
- php artisan test
- Laravelのテスト実行コマンド
どちらのコマンドでも、「どのファイルの何行目でエラーが起きたのか」とエラー内容を表示してくれますが、上記に加えて「php artisan test」は
- vendor/bin/phpunitよりもテストレポートが視覚的にわかりやすい
- 開発とデバッグを容易にするため、詳細なテストレポートを提供してくれる
という特徴があります。そのためLaravelでは、「vendor/bin/phpunit」よりも「php artisan test」がおすすめです。
テストコードの運用
テストコードは誰が書くべきなのか
基本的にはテストの対象となるコードを書いた人が書くべきです。
理由はシンプルで、そのコードを書いた人がそのコードを誰よりも理解しているため、テストコードの実装が一番早いと考えられるからです。
「どれくらいの粒度でテストをするか」を決める
テストコードを作成する前にチームで「『どれくらいの粒度でテストをするか』を決める」必要があります。 なぜなら、粒度を決めていないと、必要以上にテストコードの実装に時間を費やしてしまう可能性があるからです。 テストコードを書いた経験がある人ならば、一度は「どこまでテスト書けばいいんだろう」と頭を悩ませた経験があるかと思います。
テストコードの粒度は大きくすればするほど、つまりテストで保証する範囲が大きければ大きいほど、よりシステムの安全性を保証することができます。 しかし、テストで保証する範囲を大きくした分、メンテナンスのコストも大きくなってしまいます。
バリデーションのテストを例に考えてみます。通常のバリデーションのテストでは、エラーメッセージが表示されることまでをテストする必要はないですが、バリデーションのテストでエラーメッセージが表示されることまでをテストするとなったとしましょう。 この場合、通常のバリデーションテストに加えてエラーメッセージが表示されることも確認している、つまりテストの粒度が大きいため、確かにシステムの安全性をより保証できていると言えます。
しかし、バリデーションのパターンが多くなればなるほど、各バリデーションに対応するエラーメッセージも多くなるので、テストコードを書くコストおよびメンテナンスをするコストはより高くなってしまいます。 このようにテストコードの粒度を大きくすることは、必ずしもメリットばかりがあるわけではないのです。 そのためテストコードの粒度は「テストの目的をきちんと明確にする」ことで決めなければなりません。
先ほどあげたバリデーションのテスト例からも分かるようにテストの目的が明確であれば、どの粒度でテストすればいいかがおおよそ把握することができます。 以上の理由から、必要以上にテストコードの粒度を大きくする必要はなく、「どれくらいの粒度でテストをするか」をきちんとチームで共有してからテストコードを書くべきです。
さいごに
いかがだったでしょうか。Laravelでテストコードを導入するために必要なことがおおよそ把握できたと思います。
テストコードを書いたことが一度もない方は、ぜひこの機会にテストコードにチャレンジしていただきたいと思います。
本記事では説明しきれていないことがたくさんありますが、少しでもこの記事がテストコードの導入を検討するきっかけとなれれば幸いです。
Webサイト・システムの
お悩みがある方は
お気軽にご相談ください
出張またはWeb会議にて、貴社Webサイトの改善すべき点や
ご相談事項に無料で回答いたします。