【Laravel/Breeze】HTTPテストでCRUD操作をテストする方法
Laravel9.x系。公式ドキュメントにはHTTPテストにおけるpost()
とかの説明がほとんど無くて困ったのでメモ。
やりたいこと
顧客を新規登録したら顧客の一覧ページにリダイレクトされて,登録された顧客が index に表示されているかどうかテストしたい。
その他の操作も index に反映されるかどうかテストする。
ここではコードを理解しやすくするためにデータを配列で用意しているが,通常は factory を用いることになる。
storeメソッドのテスト
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use App\Models\User; <--入れ忘れに注意
class StudentTest extends TestCase
{
use RefreshDatabase;
protected $seed = true; //ユーザー認証を突破するためシードしておく
//テストが成功するデータを用意
$data = [
'name' => '田中太郎',
'birthdate' => '2022-07-04',
'email' => 'test@gmail.com',
];
/** @test */
public function 顧客を新規登録したら一覧にリダイレクトされる()
{
$response = $this->actingAs(User::find(1))
->post(route('customer.store'), $this->data) //postでデータを送る
->assertRedirect(route('customer.index')); //リダイレクト先を確認
}
/** @test */
public function 新規登録した顧客が一覧に反映される()
{
$response = $this->actingAs(User::find(1))
->post(route('customer.store'), $this->data)
$response = $this
->get(route('customer.index')) //indexページにアクセスする
->assertSee('田中太郎'); //登録したデータが表示されているかどうか確認
}
}
大抵はユーザー認証が必要なはずなので protected $seed = true;
でユーザーをシーダーで登録しておく(シーダーには factory などを用いてユーザーの情報を登録するコードを書いておく)。id が 1 のユーザーでログインした状態を作りたいなら,actingAs(User::find(1))
とする。
次に post()
で store
メソッドにデータを送る。通常なら create
メソッドのビューで <input>
を使ってデータを入力すると思うので,そこで入力するデータを配列として用意すると良い。コードに書いているように,メソッドの外で書いた変数や配列をメソッド内で使うときには,$this->data
のような形で書く。
コントローラー側の store
メソッドでは,return redirect()->route('customer.index');
のような感じでリダイレクトするように書かれているはずなので,assertRedirect(route('customer.index'))
でリダイレクトが返ってきているかどうかを判定する。
ちなみに post()
を実行してもページが実際にリダイレクトされるわけではなく,「このページにリダイレクトしてね」という指示文がレスポンスとして返ってくる。assertRedirect
はその指示文に指定したルートが含まれているかどうかを判定する。
データがちゃんと登録され,index
に反映されるかを確認するには,get()
を用いて,改めて index
ページにアクセスすると良い。
バリデーションエラーのテスト
バリデーションで name
が空白だとエラーになるように設定されているとする。まずは失敗するテストを書いてみる。
//テストに成功するデータ
$data = [
'name' => '田中太郎',
'birthdate' => '2022-07-04',
'email' => 'test@gmail.com',
];
/** @test */
public function 名前が空白は不可()
{
$data = $this->data; //メソッドの外で定義した配列を取り込む
$data['name'] = ''; //nameを空白にする
$response = $this->actingAs(User::find(1))
->post(route('customer.store'), $data) //postでデータを送る
->assertValid(); //バリデーションが成功しているか確認
}
このテストはバリデーションでエラーが発生するので失敗する。
Response has unexpected validation errors:
{
"name": [
"nameは、必ず指定してください。"
]
}
のような結果が返ってくるはず。
今度は成功するテストを書いてみる。
assertValid()
を assertInValid(['name' => 'nameは、必ず指定してください。'])
に書き換える。
これは assertInValid()
とすることもでき,この場合はエラーの内容は考慮されない。
今度はテストが成功するはず。
updateメソッドのテスト
updateメソッドのルートが /customer/{customer}
となっていて,id が 1 の customer を編集するテストを書いてみる。storeメソッドのときは post()
を用いたが,updateメソッドのときは put()
を用いる。
//テストに成功するデータ
$data = [
'name' => '田中太郎',
'birthdate' => '2022-07-04',
'email' => 'test@gmail.com',
];
/** @test */
public function 顧客を編集したら一覧にリダイレクトされる()
{
$response = $this->actingAs(User::find(1))
->post(route('customer.store'), $this->data); //postで新規登録
$data = $this->data;
$data['name'] = '田中次郎'; //nameを変更
$response = $this->actingAs(User::find(1))
->put(route('customer.update', ['customer' => 1]), $data) //putでデータを送る
->assertRedirect(route('customer.index')); //リダイレクト先を確認
}
/** @test */
public function 顧客を編集したら一覧に反映される()
{
//最後のアサーション以外は↑と同じ
$response = $this->actingAs(User::find(1))
->post(route('customer.store'), $this->data);
$data = $this->data;
$data['name'] = '田中次郎';
$response = $this
->put(route('customer.update', ['customer' => 1]), $data);
$response = $this->actingAs(User::find(1))
->get(route('customer.index'))
->assertSee('田中次郎'); //登録したデータが表示されているかどうか確認
}
/** @test */
public function 名前が空白は不可()
{
$response = $this->actingAs(User::find(1))
->post(route('customer.store'), $this->data);
$data = $this->data;
$data['name'] = '';
$response = $this
->put(route('customer.update', ['customer' => 1]), $data)
//バリデーションエラーを確認
->assertInValid(['name' => 'nameは、必ず指定してください。']);
}
流れとしてはいったん成功するデータを post()
で登録しておいて,データを変更したあと put()
で更新している。
destroyメソッドのテスト
データをいったん登録したあと,そのデータを delete() で削除している。
use App\Models\Customer; <--customerのモデルを書いておく
//テストに成功するデータ
$data = [
'name' => '田中太郎',
'birthdate' => '2022-07-04',
'email' => 'test@gmail.com',
];
/** @test */
public function 顧客を削除したら一覧にリダイレクトされる()
{
$response = $this->actingAs(User::find(1))
->post(route('customer.store'), $this->data); //postで新規登録
//登録したデータを取得
$customer = Customer::where('name', '田中太郎')->first();
$response = $this
//deleteでデータを削除
->delete(route('customer.destroy', ['customer' => $customer->id]))
->assertRedirect(route('customer.index')); //リダイレクト先を確認
}
/** @test */
public function 顧客を削除したら一覧に反映される()
{
//最後のアサーション以外は↑と同じ
$response = $this->actingAs(User::find(1))
->post(route('customer.store'), $this->data);
$customer = Customer::where('name', '田中太郎')->first();
$response = $this
->delete(route('customer.destroy', ['customer' => $customer->id]))
//削除したデータが表示されていないことを確認
->assertDontSee('田中太郎');
}
factoryを使う
上記をfactoryを用いたテストに書き換えてみる。factoryの作り方は省略。
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use App\Models\User;
use App\Models\Customer;
class StudentTest extends TestCase
{
use RefreshDatabase;
protected $seed = true;
/** @test */
public function 顧客を新規登録したら一覧にリダイレクトされる()
{
//factoryでデータを作って配列に変換する
$data = Customer::factory()->make()->toArray();
$response = $this->actingAs(User::find(1))
->post(route('customer.store'), $data) //postでデータを送る
->assertRedirect(route('customer.index')); //リダイレクト先を確認
}
}
post()
はリクエストを配列として送る。factoryで作られるデータはコレクションなのでtoArray()
で配列に変換しておくと良い。
/** @test */
public function 顧客を編集したら一覧にリダイレクトされる()
{
//factoryでデータを作って配列に変換する
$data = Customer::factory()->make()->toArray();
$response = $this->actingAs(User::find(1)); //ログイン状態を作る
//データを登録
//$customerには登録したデータのコレクションが格納される
//updateするときにidが必要になる
$customer = Auth::user()->customers()->create($data);
$data['name'] = '田中次郎'; //nameを変更
$response = $this
->put(route('customer.update', ['customer' => $customer->id]), $data)
->assertRedirect(route('customer.index')); //リダイレクト先を確認
}
update
メソッドのテスト。user と customer がリレーションしているとする。いったんcustomerを作って,そのデータを編集しupdateするという流れ。
customerを作るときstore
メソッドを使っても良いが,create()
を用いた方がスマートに書ける。
/** @test */
public function 顧客を削除したら一覧にリダイレクトされる()
{
$data = Customer::factory()->make()->toArray();
$response = $this->actingAs(User::find(1));
$customer = Auth::user()->customers()->create($data);
$response = $this
->delete(route('customer.destroy', ['customer' => $customer->id]))
->assertRedirect(route('customer.index'));
}
destroy
メソッドのテストは上のようになる。
SNSでシェア