laravel (sail)でテストしてまっか?(Featureテスト準備編)
テストをやるには前提が必要やからね
今
<?php
namespace App\Http\Controllers;
use App\Models\UploadedFile;
use Illuminate\Http\Request;
class UploaderController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
$uploadedFiles = UploadedFile::latest()->get();
return view('uploaders.index', ['uploadedFiles' => $uploadedFiles]);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$request->validate([
'file' => 'required|file',
]);
$file = $request->file('file');
$originalName = $file->getClientOriginalName();
$mime = $file->getMimeType();
$size = $file->getSize();
$savedName = $savedName = \Str::random(10).md5($originalName);
$data = [
'original_name' => $originalName,
'saved_name' => $savedName,
'mime_type' => $mime,
'size' => $size,
];
\DB::beginTransaction();
$uploadedFile = UploadedFile::create($data);
$extension = $file->getClientOriginalExtension();
$savedName = sprintf('%05d.%s', $uploadedFile->id, $extension);
$path = $file->storeAs('uploaded_files', $savedName, 'public');
// saved_nameを更新
$uploadedFile->update(['saved_name' => $savedName]);
\DB::commit();
// dd($request->all());
return redirect(route('uploaders.index'))
->with(['status' => __('File uploaded')]);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(UploadedFile $uploader)
{
\Storage::delete('uploaded_files/' . $uploader->saved_name);
$uploader->delete();
return redirect()->route('uploaders.index')
->with('status', __('File deleted successfully.'));
}
}
こういうコントローラーのlaravelのコードがある。まあこれ単純にファイルを保存して表示して削除するたけなんだけど、ちょっとこれでは捻りが足りないからvalidationなんかも付けてみよう。
ValidationのためのForm requestの追加
これは簡単で
artisan make:request UploadedFileRequest
とすると
app/Http/Requests/UploadedFileRequest.php というファイルができるから開く
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UploadedFileRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true; // trueにする
}
// 略
まずdefaultでfalseなので、このままだと使えないからtrueにしておく。実はここで複雑な権限のの場合を定義しておいて、それをテストしておいてもいいんだけど、ちょっとコードが複雑になるので、ここでは実行しない。
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'file' => 'required|file|image|max:300', // 300KB以下の画像
];
}
ここにruleを書く。ここでは「300キロバイト以下の画像ファイルが必須である」事を示している。validationエラーのメッセージもカスタムできるんだけど、今回はdefaultのまんまにしといた(このメッセージも、もちろんテスト可能である)。
controllerに組みこむ
app/Http/Controllers/UploaderController.php
use App\Http\Requests\UploadedFileRequest;
これをuseしておいて
// public function store(Request $request)
public function store(UploadedFileRequest $request)
と差し替えるだけだ。これで
viewもよるが、こんな感じになる。今回はこのvalidationとかを中心にテストを仕掛けていってみよう。
の前に現状のテストを確認する
まず、tests/ディレクトリを覗いてみよう
% find tests
tests
tests/Unit
tests/Unit/ExampleTest.php
tests/TestCase.php
tests/CreatesApplication.php
tests/Feature
tests/Feature/Auth
tests/Feature/Auth/RegistrationTest.php
tests/Feature/Auth/PasswordConfirmationTest.php
tests/Feature/Auth/AuthenticationTest.php
tests/Feature/Auth/PasswordResetTest.php
tests/Feature/Auth/EmailVerificationTest.php
tests/Feature/Auth/PasswordUpdateTest.php
tests/Feature/ExampleTest.php
tests/Feature/ProfileTest.php
このように、UnitとFeatureに大きく2分類される。ここではFeatureに沢山入っているのがわかるが、これはLaravel Breezeが置いていったものだ。というわけでここではUnitテストは放置して、Featureテストばっかり見ていくことにしよう。
既存のコードを少し眺めてみる
たとえば
tests/Feature/ExampleTest.php これに関してはBreezeが置いていったわけでなく最初からあるテストの雛形である
<?php
namespace Tests\Feature;
// use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*/
public function test_the_application_returns_a_successful_response(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
これはトップページ(「 / 」 )にアクセスしたときにhttpのステータスコードが200が返る事を期待している。
laravel sailで実行する
テストにおいてはlaravel sailがあるのかないのかで随分変わってきてしまう。sailが無い場合はテスト用のデーターベースを1つ起動する必要がある(あるいは、sqliteでテストするという事もある)。どういう挙動になるのかは、後で説明するとして、ここではsailを使っているとして、設定方法を解説していく。
まずdocker-compose.ymlを見てみよう
mysqlのセクションであるが
mysql:
image: 'mysql/mysql-server:8.0'
ports:
- '${FORWARD_DB_PORT:-3306}:3306'
environment:
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: '${DB_DATABASE}'
MYSQL_USER: '${DB_USERNAME}'
MYSQL_PASSWORD: '${DB_PASSWORD}'
MYSQL_ALLOW_EMPTY_PASSWORD: 1
volumes:
- 'sail-mysql:/var/lib/mysql'
- './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
networks:
- sail
healthcheck:
test:
- CMD
- mysqladmin
- ping
- '-p${DB_PASSWORD}'
retries: 3
timeout: 5s
よく見るとこんなのが仕込まれている
./vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh
これはsailでmysqlシェルにアクセスすると確認可能である
% ./vendor/bin/sail mysql
show databases;show databases;Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 326
Server version: 8.0.32 MySQL Community Server - GPL
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| performance_schema |
| simple_uploader |
| testing |
+--------------------+
4 rows in set (0.00 sec)
要するにテストでデータベースを使う場合はこのtestingを使えといっているのだ。従ってそのように書かないといけない。
.env.testingの作成
一応基本的にはガバっと.envを.env.testingにcpする
% cp .env .env.testing
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=testing
DB_USERNAME=simple_uploader
DB_PASSWORD=password
このようにDB_DATABASEにtestingをセットすればok。
そうすると、このようにテストが通るようになる
% ./vendor/bin/sail artisan test tests/Feature/ExampleTest.php
PASS Tests\Feature\ExampleTest
✓ the application returns a successful response 0.26s
Tests: 1 passed (1 assertions)
Duration: 0.33s
ただ、ここではbreezeを使った認証を利用していないので、全てのテストを通すと大量のエラーが出てくるはずだ。
% ./vendor/bin/sail artisan test tests/Feature
FAIL Tests\Feature\Auth\AuthenticationTest
⨯ login screen can be rendered 0.65s
⨯ users can authenticate using the login screen 0.05s
✓ users can not authenticate with invalid password 0.02s
⨯ users can logout 0.02s
FAIL Tests\Feature\Auth\EmailVerificationTest
⨯ email verification screen can be rendered 0.02s
⨯ email can be verified 0.02s
⨯ email is not verified with invalid hash 0.01s
FAIL Tests\Feature\Auth\PasswordConfirmationTest
⨯ confirm password screen can be rendered 0.02s
⨯ password can be confirmed 0.02s
⨯ password is not confirmed with invalid password 0.02s
FAIL Tests\Feature\Auth\PasswordResetTest
⨯ reset password link screen can be rendered 0.01s
⨯ reset password link can be requested 0.02s
⨯ reset password screen can be rendered 0.02s
⨯ password can be reset with valid token 0.02s
FAIL Tests\Feature\Auth\PasswordUpdateTest
⨯ password can be updated 0.02s
⨯ correct password must be provided to update password 0.02s
FAIL Tests\Feature\Auth\RegistrationTest
⨯ registration screen can be rendered 0.01s
⨯ new users can register 0.01s
PASS Tests\Feature\ExampleTest
✓ the application returns a successful response 0.03s
FAIL Tests\Feature\ProfileTest
⨯ profile page is displayed 0.02s
⨯ profile information can be updated 0.02s
⨯ email verification status is unchanged when the email address is unchanged 0.02s
⨯ user can delete their account 0.02s
⨯ correct password must be provided to delete account 0.02s
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
FAILED Tests\Feature\Auth\AuthenticationTest > login screen can be rendered
Expected response status code [200] but received 404.
Failed asserting that 404 is identical to 200.
at tests/Feature/Auth/AuthenticationTest.php:18
14▕ public function test_login_screen_can_be_rendered(): void
15▕ {
16▕ $response = $this->get('/login');
17▕
➜ 18▕ $response->assertStatus(200);
19▕ }
20▕
21▕ public function test_users_can_authenticate_using_the_login_screen(): void
22▕ {
// 略
これは、今認証を利用していないので削除してしまってもいい
% rm -rf tests/Feature/Auth
% tests/Feature/ProfileTest.php # プロフィールも使っていないので削除
ただまあこれはテストの書き方としては秀逸なのでこれを利用してテストの内容を見ておくのもいいんですけどね。っていう。
% ./vendor/bin/sail test
PASS Tests\Unit\ExampleTest
✓ that true is true 0.01s
PASS Tests\Feature\ExampleTest
✓ the application returns a successful response 0.26s
Tests: 2 passed (2 assertions)
Duration: 0.37s
このようにテストが通るようになった。
次回は
いよいよテストを書いていくことにしよう
この記事が気に入ったらサポートをしてみませんか?