目的
初心に返ってテスト駆動開発をして、記事にしました。
記事にすると客観的になるし、ツッコミもらえるかもしれないし…
環境
Windows10
Docker
LaravelPHP
phpunit
作成する画面
データベースに格納されているデータを取得して表示するだけです。
データもフィルタする必要もないくらい超シンプルな機能です。
実装方法
RestAPIで実装するので、APIをつくります。
このAPIを作るときにTDD(テスト駆動開発)します。
前提
dockerを利用しているため、artisanはdocker-compose execを利用しています。
詳しくはこちらを参照してください。
補足として、dokcer-compose execを利用しないコマンドも載せます。
実装手順
- テストのテンプレートを作る
- 失敗するテストを書く
- テストに失敗する
- テストが通るようにプログラミングする
- テストに成功する
- 新しいテストケースを追加する
- 2から5の繰り返し
実装
1. テストのテンプレートを作る
Laravelのartisanを利用して、テストのテンプレートを作ります。
資格種別はExaminationsというテーブルに格納されています。
テストファイル名も"ExaminationTest"とします。
$ docker-compose exec workspace php artisan make:test ExaminationTest
$ php artisan make:test ExaminationTest
"/tests/Feature/"フォルダに"ExaminationTest.php"ができます。
ここでいきなり"Feature"なの?と思った方!正しいです。
本来はAPIを実装する前にユニットテストがあるべきです。
しかし、このAPIはLaravelネイティブのメソッドを利用するため、ユニットテスト済みということにします。
APIが「Laravelネイティブのメソッド」のみであれば単体テストは不要と考えています。
※何でもかんでもテストを書くとボリュームが増えすぎて大変です。
2. 失敗するテストを書く
今回作成するAPIのURLは「/examination/getAll」とします。
まだ、コントローラもルーティングもしていないので、/examination/getAll にアクセスするとステータスコード200を返すはずです。
public function testExaminationGetAll()
{
$response = $this->json('GET', '/api/getAllExamination');
$response->assertStatus(200);
}
3. テストに失敗する
テストを実行すると失敗します(あたりまえ~)。
--filterを利用して、メソッドを特定しています。
$ docker-compose exec workspace ./vendor/bin/phpunit --filter=testExaminationGetAll
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 703 ms, Memory: 10.00MB
There was 1 failure:
1) Tests\Feature\ExaminationTest::testExaminationGetAll
Expected status code 200 but received 404.
Failed asserting that false is true.
/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:78
/var/www/tests/Feature/ExaminationTest.php:30
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
$ docker-compose exec workspace ./vendor/bin/phpunit --filter=testExaminationGetAll
4. テストが通るようにプログラミングする
コントローラが存在していないのでテンプレートを作成します。
$ docker-compose exec workspace php artisan make:controller ExaminationController
$ php artisan make:controller ExaminationController
続いて、テンプレートにgetAllメソッドを追加します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ExaminationController extends Controller
{
public function getAll()
{
}
}
ルーティングします。
//-- 試験区分をすべて取得
Route::get('/getAllExamination', 'ExaminationController@getAll');
5. テストに成功する
再度テストします。
テストがステータスコード200の確認だけなので、getAllメソッドが空でもテストは通ります。
$ docker-compose exec workspace ./vendor/bin/phpunit --filter=testExaminationGetAll
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 553 ms, Memory: 10.00MB
OK (1 test, 1 assertion)
$ ./vendor/bin/phpunit --filter=testExaminationGetAll
6. 新しいテストケースを追加する
このAPIは1件以上(現状4件)必ずデータが返ってきます。
データ件数が0件はエラーです。
したがって、「取得するJOSNを配列にすると1件以上存在すること」というテストが必要です。
public function testExaminationGetAll()
{
/*
* レスポンスを取得
*/
$response = $this->json('GET', '/api/getAllExamination');
/*
* テストケース
* 1. ステータスコード200が返ること
*/
$response->assertStatus(200);
/*
* テストケース
* 2. 取得するJOSNを配列にすると1件以上存在すること(0件はありえない)
*/
$this->assertGreaterThanOrEqual(1, count(json_decode($response->getContent())));
}
実はこのテストケースにするために試行錯誤しています。
過度なテストをしても負荷がかかりメンテナンスが大変だし、テストしなさすぎも問題です。
この試行錯誤がTDD(テスト駆動開発)で重要です!!!
※この記事はこれがいいたい!
7. 2から5の繰り返し
再度テストを実行するとエラーになります(再度失敗するテストを書いています)。
$ docker-compose exec workspace ./vendor/bin/phpunit --filter=testExaminationGetAll
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 579 ms, Memory: 10.00MB
There was 1 failure:
1) Tests\Feature\ExaminationTest::testExaminationGetAll
Failed asserting that 0 is equal to 1 or is greater than 1.
/var/www/tests/Feature/ExaminationTest.php:44
FAILURES!
Tests: 1, Assertions: 3, Failures: 1.
試験区分をすべて取得してJSONで返すように実装します。
前述しましたが、このgetAll()メソッドは、Examination::all()を返すだけです。
Examination::all()が自作のメソッドであれば、ユニットテストをします。
しかし、これはLaravelのネイティブメソッドで、フレームワークが動作を保証していると考え、ユニットテストは書きません。
class ExaminationController extends Controller
{
/**
* すべての試験区分を取得
*
*
* @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\static[]
*/
public function getAll()
{
/*
* オブジェクトをreturnするとLaravelがJSONにしてくれる
*/
return Examination::all();
}
}
テストを実行すると、成功します
$ docker-compose exec workspace ./vendor/bin/phpunit --filter=testExaminationGetAll
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 619 ms, Memory: 12.00MB
OK (1 test, 3 assertions)
このように
試行錯誤してテストケースを追加
→テスト失敗
→テストが通るようにプログラミング
→テスト成功
これを繰り返すことで、テストができる小さい、保守性・品質が高いAPIを作れます!
余談 テストの実行の仕方
tests配下のテストをすべて実行
Unitフォルダ配下ももFeatureフォルダ配下もテストが実行されます。
$ ./vendor/bin/phpunit
指定ディレクトリ配下全部実行
下記の場合Featureフォルダ配下だけ
$ ./vendor/bin/phpunit tests/Feature
クラス名を指定してテストを実行
クラスを指定できます。
$ ./vendor/bin/phpunit --filter=ExaminationTest
メソッドを指定してテストを実行
メソッドも指定できます。
テストを書いているときはこれを使うのが便利ですね。
$ php vendor/bin/phpunit --filter=visit_sample_page