ディップ Advent Calendarの17日目です。
はじめに
はじめまして。
ディップ株式会社に2018年新卒で入社し、求人系サービスの開発や社内向けツールの開発を行なっている@taku-0728です。
今回は私が業務で使っているLaravelのHTTPテストについてチュートリアル風に書いてみます。
Qiitaへの投稿は初めてゆえ至らない点もあるかと思いますが、遠慮なくコメントでご指摘いただければと思います。
やること
Laravelでテストコードを書きながらユーザの新規登録機能を作ります。
ただし今回、php artisan make:auth
コマンドは使いません。
作るもの
作るものはほぼこちらの丸パクリです。単純に作るだけならこちらのほうがわかりやすいと思います。
新規登録以外についても丁寧に書かれています。
https://laraweb.net/tutorial/1872/
開発環境
- OS:macOS Mojave 10.14.1
- Laravel:5.7
- Mysql:8.0
- Docker:Docker 2.0.0.0-mac81
準備
事前にlaradockで適当なコンテナを立ち上げてlaravelプロジェクトを作成してあること
.envファイルに適切な設定を記述してデータベースに接続できること
実装
とりあえずアクセスできるページを作ってみる
まずartisanコマンドでUserControllerを生成します
$ php artisan make:controller UserController
ルーティングを設定します
Route::get('/signup', 'UserController@signUp');
コントローラを編集します
ここではviewファイルを返すだけの処理しかかきません。
/*
省略
*/
class UserController extends Controller
{
public function signUp(){
return view('user.signup');
}
}
適当にviewファイルを作ります。
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<h1>認証画面のテスト</h1>
</body>
</html>
http://localhost/signup にアクセスしてみましょう。「認証画面のテスト」とでかでかと表示されるはずです。
テストコードを書いてみる
アクセスできるページができたので、ここで実際にテストコードを書いていきます。
Laravelのテストには2種類あり、クラスやメソッドなどモジュール単位の動作を検証するユニットテストとWebページやAPI機能を検証するフィーチャテストがサポートされています。
今回はフィーチャテストを中心に行なっていきます。
下記のコマンドを実行するとテストコードが生成されます。
$ php artisan make:test TopTest
テストコードが無事生成されました。
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class TopTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testExample()
{
$this->assertTrue(true);
}
}
これを先ほど作成したページに対するテストコードに書き換えていきます。
まず最初のテストパターンとして、「自分で作ったページにアクセスできるか」をテストしてみます。
先ほどのテストコードを以下のように書き換えます。
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class TopTest extends TestCase
{
/**
* トップページの表示内容のテスト
*
* @return void
*/
public function testExample()
{
$response = $this->get('/signup');
$response->assertStatus(200);
}
}
上記のテストコードを実行してみましょう。
$ vendor/bin/phpunit tests/Feature/TopTest.php
PHPUnit 7.5.0 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 492 ms, Memory: 12.00MB
OK (1 test, 1 assertion)
無事テストが通り、アクセスしてステータスコード200が返ってきたことが確認できました。
次はviewファイルを拡張していきます。
<html>
<head>
<meta charset="utf-8"/>
<title>チュートリアル</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/flat-ui/2.3.0/css/flat-ui.min.css" rel="stylesheet">
<link href="starter-template.css" rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 680px;">
<div class="row">
<form action="/profile" method="post" class="form-horizontal" style="margin-top: 50px;">
<div class="form-group">
<label class="col-sm-3 control-label" for="InputName">氏名</label>
<div class="col-sm-9">
<input type="text" name="name" class="form-control" id="InputName" placeholder="氏名">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="InputEmail">メール・アドレス</label>
<div class="col-sm-9">
<input type="email" name="email" class="form-control" id="InputEmail" placeholder="メール・アドレス">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="InputPassword">パスワード</label>
<div class="col-sm-9">
<input type="password" name="password" class="form-control" id="InputPassword" placeholder="パスワード">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="area1">住所(エリア)</label>
<div class="col-sm-9">
<select name="area" data-toggle="select" class="form-control select select-default" id="area1">
<option>北海道</option>
<option>東北</option>
<option>関東</option>
<option>中部</option>
<option>近畿</option>
<option>中国</option>
<option>四国</option>
<option>九州</option>
<option>沖縄</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary btn-block">新規登録</button>
</div>
</div>
{{ csrf_field() }}
</form>
</div>
</div>
</body>
</html>
入力用フォームの作成と、bootstrapを使って少し見た目を整えました。
入力用のフォームができたので登録処理に入っていきます。
登録準備
まずartisanコマンドでテーブル作成します。
php artisan make:migration create_users_table
生成されたマイグレーションファイルに以下のように追記します。
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('email')->unique();
$table->string('password');
$table->string('name');
$table->string('area');
$table->rememberToken();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
}
編集したマイグレーションファイルを実行します。
php artisan migrate
http://localhost:8080 にアクセスして、userテーブルが正しく生成されていれば成功です。
登録用テストコード生成
viewファイルに合わせてテストコードも編集していきます。
先ほど作成したテストコードを以下のように書き換えます。
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class TopTest extends TestCase
{
/**
* トップページの表示内容のテスト
*
* @return void
*/
public function testTopVisit()
{
$response = $this->get('/signup');
$response->assertStatus(200);
}
/**
* 入力されたパラメータの送信テスト
*
* @return void
*/
public function testPost()
{
$response = $this->from('/signup')->post('/profile', ['name' => 'テスト太郎', 'email' => 'test@test.com', 'password' => 'test', 'area' => '北海道']);
$this->assertDatabaseHas('users', ['email' => 'test@test.com']);
$response->assertStatus(200);
}
}
新しく「入力されたパラメータの送信テスト」を追記しました。http://localhost/signup からhttp://localhost/profile にpostリクエストを送信し、DBのusersテーブルにemailカラムがtest@test.com
のレコードが存在するか確認した後、ステータスコード200が返ってくるかをテストしています。
当然まだページを作っていないので失敗しますが上記のテストコードを実行してみます。
ここで注意点が1点あります。
http://localhost/signup からリクエストを送信する必要があるので、dockerコンテナの中に入ってコマンドを実行してください。
$ docker-compose exec workspace bash
$ vendor/bin/phpunit tests/Feature/TopTest.php
PHPUnit 7.5.0 by Sebastian Bergmann and contributors.
.F 2 / 2 (100%)
Time: 2.24 seconds, Memory: 18.00MB
There was 1 failure:
1) Tests\Feature\TopTest::testPost
Failed asserting that a row in the table [users] matches the attributes {
"email": "test@test.com"
}.
The table is empty.
/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php:24
/var/www/tests/Feature/TopTest.php:31
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
現状ではusersテーブルは空なので、当然テストは失敗します。
このテストコードが成功するようにコードを追記していきます。
登録処理
まずルーティングを追記します。
Route::post('/profile', 'UserController@profile');
ルーティングができたので、Controllerを編集していきます。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
class UserController extends Controller
{
public function signUp() {
return view('user.signup');
}
public function profile(Request $request) {
// バリデーション
$validationData = $request->validate([
'name' => 'required',
'email' => 'email|required|unique:users,email',
'password' => 'required|min:4',
'area' => 'required'
]);
// DBインサート
$user = new User([
'name' => $validationData['name'],
'email' => $validationData['email'],
'password' => bcrypt($validationData['password']),
'area' => $validationData['area']
]);
// 保存
$user->save();
return view('user.profile');
}
}
Modelも追記する必要があるので追記します。すでにapp/User.phpが存在するのでこちらを以下のように編集します。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $fillable = ['name', 'email', 'password', 'area'];
}
また、登録確認用のviewファイルを雑に作成します。
<div style="margin-top: 30px; text-align: center;">
<h3>新規登録完了しました。</h3>
</div>
Controller, Model, Viewができたので、先ほどのテストコードを再度実行してみます。
$ vendor/bin/phpunit tests/Feature/TopTest.php
PHPUnit 7.5.0 by Sebastian Bergmann and contributors.
.. 2 / 2 (100%)
Time: 1.91 seconds, Memory: 14.00MB
OK (2 tests, 3 assertions)
無事テストが通ったことが確認できました。 http://localhost:8080 にアクセスすると、usersテーブルにも値が入っていることが確認できるはずです。
http://localhost/signup で実際にフォームに値を入力して新規登録ボタンを押すと、登録完了したことが確認できるはずです。
これで新規登録機能が完成しました!
まとめ
LaravelのHTTPテストについて、新規登録機能を例に書いてみました。
しかし、今回テストとしては
- TOPページにアクセスできること
- postした任意の値をDBに正常に登録できること
の2点しか確認できていません。実際にテストコードを書く場合、もっと様々なテストパターンについてテストコードを書く必要がありますが、その際に本記事が参考になればと思います。
最後に
最後まで読んでくださった方ありがとうございました。
冒頭にも言いましたが、Qiitaへの投稿は初めてゆえ至らない点も多くあるかと思いますが、「ここはこうしたほうがいい」や、「ここが気になった」などあれば遠慮なくコメントにて教えていただければと思います。
また機会があれば新規登録機能以外についても書いてみようと思います。
本当にありがとうございました!
参考
認証画面の自作 ~新規登録~
【書籍】PHPフレームワーク Laravel Webアプリケーション開発
HTTPテスト 5.7 Laravel
Laravelでテストコードを書くためのチュートリアル