メモ > 技術 > フレームワーク: Laravel6 > テスト
テスト
■概要
テストの方針は案件ごとに決めておく
今回の方針は
・コントローラからはサービスを呼び出す(リポジトリやモデルを直接呼び出さない)
・サービスの各メソッドに対してテストを書く
・データベースアクセスなしでテストできるように、Mockeryでリポジトリのモックを作る
・HTTPリクエストで各ページの参照をテスト
とする
■テストの実行方法
テスト: テストの準備 6.x Laravel
https://readouble.com/laravel/6.x/ja/testing.html
$ sudo su -s /bin/bash - nginx
$ cd /var/www/main
$ ./vendor/bin/phpunit
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.
.. 2 / 2 (100%)
Time: 5.47 seconds, Memory: 20.00 MB
OK (2 tests, 2 assertions)
デフォルトでは、以下にあるテストが実行される
tests\Unit\ExampleTest.php
tests\Feature\ExampleTest.php
UnitとFeatureの違いは、公式サイトでは以下のように紹介されている
Unit:
ユニットテストは極小さい、コードの独立した一部をテストします。
実際、ほとんどのユニット(Unit)テストは一つのメソッドに焦点をあてます。
Feature:
機能テストは、多くのオブジェクトがそれぞれどのように関しているかとか、
JSONエンドポイントへ完全なHTTPリクエストを送ることさえ含む、コードの幅広い範囲をテストします。
以下はユニットテストの例
(以下ははじめから作成されているテスト。値の一致をテストする)
tests\Unit\ExampleTest.php
<?php
namespace Tests\Unit;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$this->assertTrue(true);
}
}
以下は機能テストの例
(以下ははじめから作成されているテスト。トップページへのHTTPリクエストをテストする)
tests\Feature\ExampleTest.php
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
■テストの作成方法
$ php artisan make:test UserServiceTest --unit
Test created successfully.
「--unit」を付けるとUnitディレクトリにテストの雛形が作成され、
付けなければFeatureディレクトリにテストの雛形が作成される
作成された雛形をもとに、独自のテストを作成するといい
テスト: テストの準備 7.x Laravel
https://readouble.com/laravel/7.x/ja/testing.html
以降で、UserServiceのテストを作成していく
■ダミーデータの作成
テスト時にダミーデータが必要な場合、Fakerを使うことでデータを作成してくれる
まずは、Fakerが作成するダミーデータを日本語にする
config\app.php
'faker_locale' => 'en_US',
↓
'faker_locale' => 'ja_JP',
以下でFactoryを作成できる
ただし UserFactory ははじめから作成済になっている
$ php artisan make:Factory UserFactory
内容は以下のとおり。ダミーのユーザデータを作成してくれる
ただし今回のプログラムに合わせて、「use App\User;」を「use App\Models\User;」に変更している
database\factories\UserFactory.php
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\Models\User;
use Faker\Generator as Faker;
use Illuminate\Support\Str;
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/
$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
});
[Laravel5.1]Fakerチートシート - Qiita
https://qiita.com/tosite0345/items/1d47961947a6770053af
■テストの編集
先の手順において
$ php artisan make:test UserServiceTest --unit
で作成したテストを元に作り込むものとする
PHPUnit\Framework\TestCase (PHPUnitのクラス)を
Tests\TestCase (Laravelのクラス)に差し替えることにより、
Laravelのファサードなどが使えるようになる
Laravelで "A facade root has not been set." というエラーが出た場合の対処法 - Qiita
https://qiita.com/aminevsky/items/784573234d071c769288
Fakerで作成したデータを各メソッドで使い回す場合など、
共通の初期処理が必要なら setup() で行う
PHPUnitのsetUp()を使ったら「must be compatible」とエラーが表示される - Qiita
https://qiita.com/stoneBK7/items/fcc898f38ee161b38ef4
tests\Unit\UserServiceTest.php
<?php
namespace Tests\Unit;
//use Illuminate\Foundation\Testing\RefreshDatabase;
//use PHPUnit\Framework\TestCase;
use Tests\TestCase;
class UserServiceTest extends TestCase
{
private $users;
/**
* Setup the test environment.
*
* @return void
*/
protected function setUp(): void
{
parent::setUp();
// ダミーデータを作成
$this->users = factory(\App\Models\User::class, 3)->make();
/*
foreach ($this->users as $user) {
echo '[' . $user->name . ']';
echo '[' . $user->email . ']';
}
*/
}
/**
* Clean up the testing environment before the next test.
*
* @return void
*/
protected function tearDown(): void
{
parent::tearDown();
// ダミーデータを削除
$this->users = null;
}
/**
* 1件取得
*
* @return void
*/
public function testGetUser()
{
// 1件取得
$user = $this->users;
// リポジトリのモックを作成
$userRepositoryMock = \Mockery::mock(\App\Repositories\UserRepository::class);
$userRepositoryMock->shouldReceive('find')->andReturn($user);
// リポジトリをモックに差し替えてサービスを作成
$userService = new \App\Services\UserService(
$userRepositoryMock
);
$this->assertEquals($user, $userService->getUser(1));
}
/**
* 検索して取得
*
* @return void
*/
public function testGetUsers()
{
// リポジトリのモックを作成
$userRepositoryMock = \Mockery::mock(\App\Repositories\UserRepository::class);
$userRepositoryMock->shouldReceive('search')->andReturn($this->users);
// リポジトリをモックに差し替えてサービスを作成
$userService = new \App\Services\UserService(
$userRepositoryMock
);
$this->assertEquals($this->users, $userService->getUsers());
}
/**
* 件数を取得
*
* @return void
*/
public function testCountUsers()
{
// リポジトリのモックを作成
$userRepositoryMock = \Mockery::mock(\App\Repositories\UserRepository::class);
$userRepositoryMock->shouldReceive('count')->andReturn(count($this->users));
// リポジトリをモックに差し替えてサービスを作成
$userService = new \App\Services\UserService(
$userRepositoryMock
);
$this->assertEquals(3, $userService->countUsers());
}
/**
* 登録
*
* @return void
*/
public function testStoreUser()
{
// リポジトリのモックを作成
$userRepositoryMock = \Mockery::mock(\App\Repositories\UserRepository::class);
$userRepositoryMock->shouldReceive('save')->andReturn(new \App\Models\User);
// リポジトリをモックに差し替えてサービスを作成
$userService = new \App\Services\UserService(
$userRepositoryMock
);
$postRequest = new \App\Http\Requests\StoreUserRequest();
$postRequest->merge([
'name' => 'Taro Yamada',
'email' => 'taro@example.com',
'password' => 'abcd1234',
]);
$this->assertInstanceOf('\App\Models\User', $userService->storeUser($postRequest));
}
/**
* 編集
*
* @return void
*/
public function testUpdateUser()
{
// リポジトリのモックを作成
$userRepositoryMock = \Mockery::mock(\App\Repositories\UserRepository::class);
$userRepositoryMock->shouldReceive('save')->andReturn(new \App\Models\User);
// リポジトリをモックに差し替えてサービスを作成
$userService = new \App\Services\UserService(
$userRepositoryMock
);
$postRequest = new \App\Http\Requests\StoreUserRequest();
$postRequest->merge([
'name' => 'Taro Yamada',
'email' => 'taro@example.com',
'password' => 'abcd1234',
]);
$this->assertInstanceOf('\App\Models\User', $userService->storeUser($postRequest, 1));
}
/**
* 削除
*
* @return void
*/
public function testDeleteUser()
{
// リポジトリのモックを作成
$userRepositoryMock = \Mockery::mock(\App\Repositories\UserRepository::class);
$userRepositoryMock->shouldReceive('delete')->andReturn(new \App\Models\User);
// リポジトリをモックに差し替えてサービスを作成
$userService = new \App\Services\UserService(
$userRepositoryMock
);
$this->assertInstanceOf('\App\Models\User', $userService->deleteUser(1));
}
}
プログラム内の以下の部分で、ダミーデータを作成している
factory(\App\Models\User::class, 3)->make();
以下のように make() ではなく create() を使うと、実際にデータベースにも登録される
factory(\App\Models\User::class, 3)->create();