Laravelで写真関係のテストを実装してみた

2021.01.18

見出し画像

ユーザーのアバター画像とアイテム投稿の際の写真の投稿に関するテストを書いたので備忘録的に残していきます。

アイテム投稿の写真は以前の記事を参考にしているのでぜひ読んでみてくだい。ItemテーブルとImageテーブルに分かれています。

実装すること

factoryで写真テーブルを作成

ユーザーのアバター写真の投稿に関するテスト

アイテム投稿の際の写真部分に対するテスト

この辺りを書いていきます。テストは3つくらいですがfactoryを作成したり編集したりします。

前提

Itemのテストは実装されている前提で進めます。実装している記事。

ユーザーについても一連のテストは実装されている前提で進めます。実装した記事。

写真の投稿についてはbase64で処理、実装しています。実装記事

予習、新しく出てくるもの

公式に載っているファイルアップロードのテストというところに書いてあります。今のイメージはUploadedFileはそのまま写真の保存とかに使いそうな雰囲気。Storageは保存先とかを設定するのかな?といったところですが、使っていきましょう。

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

大まかな流れ

factoryファイルの作成(Image)

factoryファイルの作成(User)

ユーザーのアバター写真が写真データでなければ保存できない事

正しい写真データであればアイテムを投稿出来る事

写真データがbase64データじゃない時は保存できない事

こんな感じで書いていきます。

factoryファイルの作成(Image)

UploadedFile::fake()などを使って写真データを作成、データベースに保存していきます。

<?php

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

use App\Image;
use Faker\Generator as Faker;
use Illuminate\Http\UploadedFile;    1

$factory->define(Image::class, function (Faker $faker) {

2  $file = UploadedFile::fake()->image('item.jpg'); 
3  $path = $file->store('public');
4  $read_temp_path = str_replace('public/', '/storage/', $path);

   return [
5      'image_path' => $read_temp_path,
6      'item_id' => function() {
           return factory(Item::class)->create()->id;
       },
   ];
});

1,  use Illuminate\Http\UploadedFileを読み込みます。公式参考記事

2,  UploadedFileにfakeを合わせる事で写真を用意出来ちゃいます。便利。

3,  store()メソッドでpublic以下に写真を保存します。

4,  データベースには写真保管場所までのパスを保存しますがシンボリックを貼ってstorageから取得出来るようにしたいのでstr_replace()で書き換えています。

5,  パスを保存します。image_pathの部分にはカラム名を指定します。

6,  Itemのidを入れます。呼び出された時にitem_idの指定がない場合はfactoryを呼び出してitem作り設定します。

factoryファイルの作成(User)

ユーザー作成時にアバター写真を作成するように設定します。

use Illuminate\Http\UploadedFile;          

$factory->define(User::class, function (Faker $faker) {

   // 写真を作成する。渡す情報は写真までのパス。
1  $file = UploadedFile::fake()->image('avatar.jpg');     
   $path = $file->store('public');                          
   $read_temp_path = str_replace('public/', '/storage/', $path);

   return [
       'name' => $faker->name,
2      'avatar' => $read_temp_path,
       'profile' => $faker->text,
       'url' => '$faker->text',
       'email' => $faker->unique()->safeEmail,
       'email_verified_at' => now(),
       'password' => bcrypt('Test1234'), // password
       'remember_token' => Str::random(10),
   ];
});

1,  UploadedFileとfakeを合わせて写真を作成する。

2,  avatarとして定義した$read_temp_pathを設定する。

テストするときの実行コマンド

だいたい上でいつもやってます。若干リアクション違います(見た目の)

なんかvendor/bin/phpunitの方が良いらしいです。(kuwasikuwakaranaiww)

vendor/bin/phpunit --testdox 
もしくは
vendor/bin/phpunit --testdox ファイル名


または

php artisan test

ユーザーのアバター写真が写真データでなければ保存できない事(編集時です)

プロフィール編集時に更新する写真のデータでなければ保存出来ない事を確認するテストです。

use RefreshDatabase;

public function setUp(): void
{
   parent::setUp();

   $this->user = factory(User::class)->create();
}

public function test_選択された写真が写真データでなければ保存出来無い()
{
   $response = $this->actingAs($this->user);
1  $response = $this->get(route('user_edit', ['user' => $this->user->id]));
   $response->assertStatus(200);

2  $update_path = route('user_update', ['user' => $this->user->id]);

3  $update_data = [
       'avatar' => 'Test1234',
   ];

4  $response = $this->post($update_path, $update_data);

   // 求めるエラーメッセージ
5  $response->assertSessionHasErrorsIn("正しい写真ファイルを使用してください");

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

7  $response->assertRedirect(route('user_edit', ['user' => $this->user->id])); 
}

1,  ユーザー編集ページに移動します。

2,  編集アクションを動かすためのパスを定義します。

3,  avatarに写真データではなく文字列を渡します。ここが今回のテストです。

4,  これで編集アクションに飛ばします。

5,  assertSessionHasErrorsIn()で求めるエラー文が含まれているか確認します。リダイレクトで返ってきているのでassertSee()は使えません。

6,  リダイレクトでviewが表示されているかを確認します。

7,  リダイレクトされているリンクがユーザーの編集ページである事を確認します。

正しい写真データであればアイテムを投稿出来る事

写真の投稿についてはこちらの記事で書いているのでぜひ参考にしてみてください。

写真データがbase64コードの時に正しく処理される事を確認するテストです。

use Illuminate\Http\UploadedFile;         1
use Illuminate\Support\Facades\Storage;      2

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

   // 写真データの作成。base64コードを求めている。
3  $file = UploadedFile::fake()->image('item.jpg');
4  $file_ext = $file->getClientOriginalExtension();
5  $data = base64_encode(file_get_contents($file));
6  $src = 'data: ' . $file_ext . ';base64,' . $data;
   $itemdata = [
       'age' => 3,
       'sex' => 2,
       'area' => 5,
       'cat' => 3,
       'title' => '$fakertext_example',
       'content' => '$faker->nameeee',
       'status' => 1,
7      'images' => $src,
   ];

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

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

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

   $response->assertRedirect('/'); 

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

   // 写真データが存在することを確認
12 $get_image = Image::get()->first();

   // 取得した値のstorage部分を削除して、publicに保存されているか確認する。
13 $image_path = str_replace('/storage/', '', $get_image->image_path);

   // publicに保存されているか確認している。
14 Storage::disk('public')->assertExists($image_path);

   // データベースにも値が保存されているか確認する。
15 $this->assertDatabaseHas('images', ['image_path' => $get_image->image_path]);

}

1,  UploadedFileを使用出来るように読み込みます。

2,  こちらも同じくStorageを使えるように呼び込みます。

3,  UploadedFileとfakeを合わせて写真を作成します。写真名はテキトーです。

4,  getClientOriginalExtension()メソッドでファイルの拡張子を取得します。

5,  base64_encode()でデータの形式を変更します。

6,  コントローラーにおくるbase64の値を作成し、$srcと定義します。

7,  6で設定した$srcをimagesとしてコントローラーに送ります。

8,  投稿アクションまでのパスをroute()メソッドを使って定義します。

9,  6,8で定義した情報、パスを持ってコントローラーの投稿アクションに飛ばします。

10,  assertSessionHasNoErrors()メソッドでエラーメッセージがない事を確認します。

11,  assertDatabaseHas()メソッドで投稿したitemがデータベースに存在するか確認しています。

12,  写真データも取得出来る事を確認、取得。

13,  保存されているパスからstorageの部分を無くして定義し直す。

14,  Storage::disk(‘public’)でpublicフォルダーの中に、13で定義したパスが存在するかどうかをassertExists()メソッドを使って確認しています。

15,  最後にassertDatabaseHas()でimagesテーブルにも写真が保存されている事を確認していきます。

写真データがbase64データじゃない時は保存できない事

保存データがbase64ではないときにバリデーションが働くかを確認するテストです。今回の投稿の場合はbase64でバリデーションかけてますがファイルアップロードで実装している場合は変更する部分です。

base64で写真投稿する記事

public function test_写真データがbase64データじゃない時は保存できない() 
{
   // 認証ユーザーにして、作成ページに行ける事
   $response = $this->actingAs($this->user)->get(route('items.create'));
   $response->assertStatus(200);
   $response->assertViewIs('items.create');

   // 写真データの作成。base64コードではなく写真データを送信している。
1  $file = UploadedFile::fake()->image('avatar.jpg');
   // https://fippiy.hatenablog.jp/entry/2019/04/29/060000#%E3%83%86%E3%82%B9%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B8%E3%81%8F
2  $itemdata = [
       'age' => 3,
       'sex' => 2,
       'area' => 5,
       'cat' => 3,
       'title' => '$fakertext_example',
       'content' => '$faker->nameeee',
       'status' => 1,
       'user_id' => $this->user->id,
3      'images' => $file,
   ];

   $url = route('items.store');

4  $response = $this->post($url, $itemdata);

5  $response->assertSessionHasErrorsIn("正しい写真データを入力してください"); 

   $response->assertStatus(302);

   // リダイレクトでページ遷移している事
6  $response->assertRedirect(route('items.create'));
}

1,  UploadedFileとfakeを合わせて写真を作成する。

2,  投稿するitemの情報を設定します。

3,  Imagesには1で作成した$fileを設定します。

4,  設定した情報を持ってコントローラーに送り投稿処理します。

5,  さっきも出てきた、assertSessionHasErrorsIn()で求めるエラー文が含まれているか確認します。リダイレクトで返ってきているのでassertSee()は使えません。

6,  リダイレクトで戻っているページが投稿ページになっているかを確認します。

復習、新しく出てきたもの

以下の2つについて。

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

UploadedFileはfakerと一緒に使う事で写真データを作成することができる。なので写真データが欲しくなった時に使おう。テストとかシーダーとか。

Storageは写真の保存先を指定する事が出来る。今回は写真がpublicに保存したが場合によって適宜変更していく。

まとめ

写真についてのテストもフォルダーの指定とかが出来ていれば他の部分と変わらないかなと思います。

ただ、写真の保存の仕方によってどんな値をコントローラーに飛ばすのか?っていうところは変わってくる部分ですね。今回はbase64でした。

少しづつテストの流れが見えて来た気がしますw

今回はこんな感じで!

以上!