サイトアイコン 上尾市のWEBプログラマーによるブログ

「PHPフレームワーク Laravel Webアプリケーション開発」の感想・備忘録11

テスト

ユニットテストとフィーチャテスト

Laravelでは2つのテストがサポートされている。

  1. ユニットテスト:メソッドを検証する。
  2. フィーチャテスト(WebAPIテスト):API機能を検証する。

ユニットテスト

ユニットテストでは、テストクラスはTests\TestCaseを継承して実装する。
Tests\TestCaseはPHPUnitのPHPUnit\Framework\TestCaseを継承していて、PHPUnitの機能に加えてLaravel独自の機能が追加されている。
※ Laravel6からはTests\TestCaseではなく、直接PHPUnit\Framework\TestCaseを継承するようになった。

ユニットテストの手順

1. php artisan make:test HogeClientServiceTest --unit

tests/Unitにクラスが生成される。

2. testXxxメソッドまたは@testアノテーションを付けたメソッドを実装する。

public function testRun()
{
    $service = app(HogeClientService::class);
    $result = strpos($service->run('https://google.com/'), '<html') !== false;
    $this->assertTrue($result);
}

3. ./vendor/bin/phpunit tests/Unit/HogeClientServiceTest.phpでテスト実行。

※ 引数なしで./vendor/bin/phpunitとすると全てのテストが実行される。

データプロバイダ

テストコードに渡すパラメータをまとめて定義することができる。
2次元配列を返すメソッドを定義し、テストメソッドに@dataProviderアノテーションで指定する。

/**
 * @dataProvider dataProviderForHogeClientService
 */
public function testRun($url, $expected)
{
    $service = app(HogeClientService::class);
    $result = strpos($service->run($url), $expected) !== false;
    $this->assertTrue($result);
}

public function dataProviderForHogeClientService(): array
{
    return [
        'Googleテスト' => ['https://google.com/', '<html'],
        'Yahooテスト' => ['https://yahoo.co.jp/', '<html'],
    ];
}

例外のテスト

3つの方法がある。

  1. try/catchを使い例外をcatchして、assertInstanceOfメソッドで判定する。
  2. expectExceptionメソッドで期待する例外を指定してから対象コードを実行する。
  3. @expectedExceptionアノテーションで期待する例外を指定する。

1, 2はテストメソッド内にコードを追加する必要があるため、著者は3をオススメしている。
@expectedExceptionアノテーションを使うと、テストメソッド内は対象コードの実行のみになるので可読性が上がるため。

/**
 * @expectedException HogeException
 * @expectedExceptionMessage urlが不正
 */
public function testRun($url, $expected)
{
    $service = app(HogeClientService::class);
    $result = strpos($service->run($url), $expected) !== false;
    $this->assertTrue($result);
}

前処理と後処理

setUpメソッドに前処理、tearDownメソッドに後処理を記述する。
テストメソッドごとではなく、テストクラスごとの前処理はsetUpBeforeClassメソッド、後処理はtearDownAfterClassメソッドに記述する。

テストクラスがTests\TestCaseを継承している場合は、parent::setUp();など親クラスのメソッドもコールしなければならない。
PHPUnit\Framework\TestCaseを直接継承している場合は不要。

public static function setUpBeforeClass()
{
    parent::setUpBeforeClass();
    echo __METHOD__, PHP_EOL;
}

protected function setUp()
{
    parent::setUp();
    echo __METHOD__, PHP_EOL;
}

/**
 * @test
 */
public function テストメソッド1()
{
    echo __METHOD__, PHP_EOL;
    $this->assertTrue(true);
}

データベーステスト

  1. テスト用データベースを生成
    mysqladmin create app_test
  2. phpunit.xmlに<env name="DB_DATABASE" value="app_test">を追記。
    ※ phpunit.xmlはアプリケーションディレクトリ直下に用意されている。
  3. テストクラスにuse RefreshDatabaseを追記。
    テスト実行時にマイグレーションも実行される。
  4. テストメソッドやsetUpメソッドで、対象コードの実行前にFactoryを使ってデータを登録し、対象コードの実行後にassertDatabaseHasメソッドやassertDatabaseMissingメソッドで判定する。
factory(User::class)->create([
    'id' => 1,
    'name' => 'hoge',
]);

// 対象コードをここで実行

$this->assertDatabaseHas('users', [
    'id' => 2,
    'name' => 'hogehoge',
]);

フィーチャテスト

  1. php artisan make:test HogeTest
    tests/Featureにクラスが生成される。
  2. 継承したメソッドを使ってリクエストを送信する。
    get, post, getJson, postJson, putなど。
    $response = $this->get('https://www.sony.co.jp/');
    ※ 戻り値はTestResponse型
  3. TestResponseのアサーションメソッドを使って判定する。
    assertStatus, assertSuccessful, assertRedirect, assertHeader, assertHeaderMissing, assertJsonなど。
    $response->assertSuccessful();
    $response->assertSee('<html');

ログ

ログ出力メソッド

以下の3つからログ出力メソッドをコールすることができる。

  1. \Logファサード
  2. サービスコンテナで’log’を解決する
  3. loggerヘルパ関数

通常は\Logファサードを使う。
\Log::debug('test');

第2引数に配列で情報を渡すことができる。
\Log::info('hoge', ['id' => 33]);

ログ出力設定

Laravel5.5まではconfig/app.phpだったが、5.6からはconfig/logging.phpに変わった。
デフォルトでは以下の設定なのでstorage/log/laravel.logに出力される。

 'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['single'],
        'ignore_exceptions' => false,
    ],
    // 以下省略

'channels' => ['daily'],とすると、日毎にファイルを分けてログを出力してくれる。

テスト駆動開発

テスト駆動開発の手順

  1. テストを作成する。
  2. テストが失敗することを確認する。
  3. 最低限の実装を行い、テストが成功することを確認する。
  4. テストが失敗しないことを確認しながらリファクタリングする。

実装例

「api/hogeへGETでアクセスできるAPI」の場合。

1. テストクラスの作成。

php artisan make:test ApiHogeTest

2. テストメソッドの実装。

public function testHoge()
{
    $response = $this->get('/api/hoge');
    $response->assertStatus(200);
}

3. テストの実行。

./vendor/bin/phpunit tests/Feature/ApiHogeTest.php
テストが失敗することを確認する。

4. 実装の追加。

4-1. route/api.phpにルートを追加。

Route::get('/hoge', 'Api\HogeController@index');

4-2. コントローラの作成。

php artisan make:controller Api/HogeController

4-3. indexメソッドの実装。
public function index()
{
    echo 'OK';
}

5. テストの実行。

./vendor/bin/phpunit tests/Feature/ApiHogeTest.php
テストが成功することを確認する。

モバイルバージョンを終了