LaravelのCRUD機能をテストしてみた

2021.01.18

見出し画像

今回は前回ログイン関係のテストを実装したので、今回はCRUD機能のテストを実装していきたいと思います。

バージョン

Laravel 7.x

PHP 7.4.8

どこまで実装するか?

ファクトリーを使用したテストが実行できる。

CRUDそれぞれのテストが正しく通る。

大まかな流れ

ファクトリーを用意

テストファイル作成

Createについて(Readも)

Updateについて

Deleteについて

こんな感じで1つ1つの機能を見ていきます。

ファクトリーを用意

コマンドを打ち込んでfactoryファイルを作成します。

php artisan make:test ItemManagementTest

作成したファイルの中身を編集。中身は適宜変更してください。$faker->nameを使っていたり、$faker->textを文字列で使っていますが気にせず進めてください。

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Item;
use App\User;
use Faker\Generator as Faker;

$factory->define(Item::class, function (Faker $faker) {
   return [
       'user_id' => function() {
           return factory(User::class)->create()->id;
       },
       'title' => '$faker->text',
       'content' => $faker->name
   ];
});​

テストファイル編集

setUp()で毎回それぞれのアクションが実行される前に処理されるものを書いていきます。

ユーザーはテスト用に2つ用意します。作成するitemはuser->idが付与されます。

use RefreshDatabase;

public function setUp(): void
{
   parent::setUp();
   // テストユーザ作成
   $this->user = factory(User::class)->create();
   $this->another_user = factory(User::class)->create();
   $this->item = factory(Item::class)->create(['user_id' => $this->user->id]);

}

テスト実行コマンドは以下です

./vendor/bin/phpunit --testdox tests/Feature/ファイル名.php

Createについて(Readも)

ログインしていれば投稿ページに遷移出来、投稿出来る。データベースにも保存されていることを確認できる。かつ一覧ページに遷移すると保存した投稿のタイトルが表示されている。といった流れを書いています。

public function test_ログインしていれば投稿出来る() 
{
   // 認証ユーザーにして、作成ページに行ける事
1  $response = $this->actingAs($this->user)->get(route('items.create'));
   $response->assertStatus(200);
2  $response->assertViewIs('items.create');

3  $itemdata = [
       'title' => '$fakertext_example',
       'content' => '$faker->nameeee',
   ];

   $url = route('items.store');
4  $response = $this->post($url, $itemdata);

5  $response->assertSessionHasNoErrors(); // エラーメッセージがないこと

   $response->assertStatus(302); // リダイレクト

6  $response->assertRedirect('/'); 

   // 保存したitemがデータベースに存在するか確認。
7  $this->assertDatabaseHas('items', ['title' => '$fakertext_example']);

   $response = $this->get('/');

   $response->assertStatus(200);

8  $response->assertSeeText('一覧');

   // 一覧ページに移動
9  $response = $this->get(route('items.index'));

   $response->assertStatus(200);

10 $response->assertViewIs('items.index');

   // 先ほど投稿したitemのtitleと一致するものが表示されているか
11 $response->assertSeeText($itemdata['title']);
}

1,  ユーザーをログイン状態にして、投稿ページに遷移させます。

2,  遷移先が投稿ページであるか確認します。

3,  投稿する値を定義します。

4,  3で定義した情報を持ってrouteメソッドでコントローラー側にpostアクションとして飛ばします。

5,  投稿が上手くいき、エラーメッセージが無い事を確認します。

6,  投稿した後はリダイレクトでホームの画面に戻って来ているか確認します。

7,  保存したitemがデータベースに存在しているか確認します。引数はテーブル指定、カラム指定、期待する値。って感じです。

8,  ホームのページに一覧と言う文字があるかどうか確認しています。(ボタン)

9,  ここからはreadの処理になります。一覧ページに移動します。

10,  一覧ページが表示されているか確認します。

11,  先ほど保存したitemの情報を含んだものがviewに含まれるか確認しています。

Updateについて

updateは投稿したitemが自分のものであれば編集ページに遷移出来て、編集作業が出来る、といった流れです。データベースには編集したitemが存在している形です。

public function test_ログインしているかつ、自分の投稿であれば編集出来る() 
{
1  $response = $this->actingAs($this->user);
2  $response = $this->get(route('items.edit', ['item' => $this->item->id]));
   $response->assertStatus(200);

3  $itemdata = [
       'title' => '編集成功',
       'content' => 'これが出来たらご飯食べる',
       // 以下2つは選択しないと弾かれます。
   ];

   // updateアクションまでのパスを作成
4  $update_url = route('items.update', ['item' => $this->item->id]);
   // $this->putで編集処理
5  $response = $this->put($update_url, $itemdata);
   // エラーメッセージがないこと
6  $response->assertSessionHasNoErrors(); 
   // リダイレクト
7  $response->assertStatus(302); 
   // リダイレクトした時のurlリンク
8  $response->assertRedirect('/items/'.$this->item->id); 
   // 編集したレコードが存在するか
9  $this->assertDatabaseHas('items', ['title' => '編集成功']);
}

1,  ユーザーをログイン状態にします。

2,  parentメソッドで作成したitemの編集ページにアクセスします。

3,  編集する値を定義します。

4,  updateアクションを動かすためのルートを設定、定義します。

5,  putメソッドで3,4の情報を持ってupdate処理に飛ばしています。

6,  編集は問題なく済み、エラーメッセージが無い事。

7,  編集が終わった後はリダイレクト処理で302ステータスである事を確認します。

8,  編集が済んだ後はリダイレクトで詳細ページに戻ってくるようになっているか確認します。

9,  データベースに編集した値が存在しているか確認しています。期待する値は3で設定したものです。

Deleteについて

投稿したitemが自分のものであれば、詳細ページに編集と削除ボタンが存在する事、そして削除の処理が実行出来る。実行した後はデータベースに存在していないことを確認する。こんな流れになっていますね。

public function test_ログインしているかつ、自分の投稿であれば削除出来る() 
{
   // showページにアクセスした時にボタンが存在していること == 自分の投稿&ログインしている。
1  $response = $this->actingAs($this->user);
2  $response = $this->get(route('items.show', ['item' => $this->item->id]));
3  $response->assertSee('編集', '削除');
   $response->assertStatus(200);

   // 存在の確認
4  $this->assertDatabaseHas('items', ['id' => $this->item->id]);

5  $delete_url = route('items.destroy', ['item' => $this->item->id]);
   // 削除で飛ばす
6  $response = $this->delete($delete_url);
   $response->assertSessionHasNoErrors(); 
   // リダイレクト
7  $response = $this->get('/items');
   $response->assertStatus(200);

   // 消された事
8  $this->assertDeleted($this->item);

   // 存在しない事
9  $this->assertDatabaseMissing('items', ['id' => $this->item->id]);
}

1,  ユーザーをログイン状態にします。いつもと同じです。

2,  商品の詳細ページにアクセスします。itemについてはparentメソッドで作成したものです。

3,  詳細ページにアクセス出来ていて、かつ編集、削除ボタンが存在している事を確認します。

4,  削除前にそのitemがデータベースに存在するか確認しています。

5,  destroyアクションに飛ばすためのパスをrouteメソッドを使用して定義します。

6,  5で定義したものを実引数としてdeleteアクションでコントローラー側に飛ばします。

7,  削除された後は一覧ページに遷移しているか確認します。

8,  assertDeletedメソッドでitemが削除されている事を確認します。

9,  削除したitemがデータベースに存在しないことを確認します。

まとめ

updateやdeleteなどはファクトリーでユーザーとアイテムを作成出来る様にし、テストファイルのparent部分で呼び出して使う。

異常系はまだ書いてませんが、紹介している異常系の書き方と似てくるのかなと思います。

初めてやりましたが、テスト良いですね。全部通った時の爽快感がww

今回はこんな感じで!

以上!