実務1年目駆け出しエンジニアがLaravel&ReactでWebアプリケーション開発に挑戦してみた!(その43)
0. 初めに
こんにちは!
0.1 本シリーズについて
このシリーズでは、見習いエンジニアの学習過程としてWebアプリケーション開発の方法をゼロから解説しています!
「プログラミング言語の基本文法ができるようになったけど、次は何を勉強してい良いかわからない...」
「LaravelやReactなどのモダンな技術を使ってWebアプリケーション開発に挑戦してみたい!」
そんな方はぜひ最初から見てみてください!
要件定義・設計編(全1回)
アプリケーションの機能や画面など、開発するうえで必要なものを文章化しました。
環境構築編(全4回)
開発するために必要な環境を構築します。
Dockerでサーバーを構築して、Laravelをインストールしました。
バックエンド実装編(全16回)
インストールしたLaravelでバックエンドの機能を実装しました。
フロントエンド実装編(全22回)
Reactを用いてフロントエンドを実装しました。
Inertia.jsという便利なライブラリを使いました。
テスト・デバッグ編
そして、今日からテスト・デバッグをしていきたいと思います!
0.2 テストとは
テストはアプリケーションの品質を高めるために必須なものと言われています!
手動で画面を操作して行うものもあれば、テストコードと呼ばれるプログラムを作成して自動で行うものなどいろいろあります。
他にも、テストの種類や技法は調べるとたくさん出てきて、結構奥が深いことが分かります。
その中でも今回は、Unitテスト、Featureテスト、E2Eテストの3つに取り組みたいと思います!
テストを行うことは、アプリケーションの品質を高めるために重要!
0.3 今回取り組むテスト
Unitテスト
いわゆる、単体テストに相当するものです。
アプリケーションの処理一つ一つを確認するためのものです!
PHPUnitと呼ばれるテストツールをベースにしています。
Featureテスト
その名の通り、機能全体の一連の動きをテストします。
例: 「登録ボタンを押す」→「DBに保存される」→「画面が更新される」
Unitテストが一つ一つの調味料や食材をテストするなら、Featureテストはそれらが組み合わさった調理された料理をテストするようなイメージです。
Laravel独自のクラスを継承して作成するため、Unitテストと比べて処理時間が遅くなる(裏でLaravelがいろいろしてくれるので)らしいです。
主な違いをまとめてみました。
| Unitテスト | Featureテスト | |
|---|---|---|
| 対象 | 部品一つ | 機能全体 |
| 速さ | 速い | 遅い |
| DB使用 | 基本しない | する |
E2Eテスト
End-to-Endテストの略で、実際にユーザーがブラウザを開いて画面上で操作するのを自動化するものです。
Featureテストが料理の味見なら、E2Eテストは実際にお店に身内を呼んで注文を受けて調理・提供、後かたずけまで一通りテストしてみるイメージです。
Featureテストよりもさらに遅くて不安定なため、何でもかんでもE2Eテストでテストしようとすると非効率です。
そのため、これら3種類のテストをうまく切り分けて実施していくことがカギになりそうです!
E2Eテストのためのツールはたくさんありますが、今回は最近評判が良くて僕も少しだけ経験があるPlaywrightを使ってみたいと思います!
確かにE2Eテストは実際の使用感に近い。しかし、効率の観点から何でもかんでもE2Eテストで行うことはしてはダメ。
1. ブランチ運用
Unitテスト、Featureテスト、E2Eテストの順に行っていき、不具合が見つかったら修正していくという感じでやっていきたいと思います!
今日は、Unitテストに入る前のデータの準備をしたいと思います。
先ほど、Unitテストは "基本的には" DBは使用しないと述べましたが、一部ではやはり使うこともありそうですし、Featureテストでは使用するのでこの段階で用意します(後述: 結局使いませんでしたw)。
まずは、ローカルリポジトリのdevelopブランチにチェックアウトします。
実行コマンド
$ git checkout develop
前回使用したブランチはもう不要なので削除します(もし未削除なら)。
実行コマンド
$ git branch -D refactor/frontend/unnecessary-components
実行コマンド(確認用)
$ git branch
developブランチを最新化させます。
実行コマンド
$ git pull origin develop
新しいブランチを切って作業開始です!
実行コマンド
$ git checkout -b test/unit/db-set-up
※ブランチ名は、自分が分かりやすければ自由に変えてもらっても大丈夫です!
2. テスト用DB設定
2.1 phpunit.xml修正
srcディレクトリ直下にphpunit.xmlというファイルがあることを確認してください。
環境構築編でLaravelをインストールした時に自動で生成されているはずです。
testsuites
軽く中身を見てみましょう。
<testsuites>
<testsuite name="Unit">
<directory>tests/Unit</directory> <!-- Unitテストの格納先 -->
</testsuite>
<testsuite name="Feature">
<directory>tests/Feature</directory> <!-- Featureテストの格納先 -->
</testsuite>
</testsuites>
testsuitesにはUnitテストとFeatureテストの格納先が設定されています。
source
<source>
<include>
<directory>app</directory> <!-- app/ 以下のコードがカバレッジ対象 -->
</include>
</source>
ここには、カバレッジ対象(テストの範囲)が設定されています。
php
環境変数(.envファイルの中身)が上書きされます。
今回修正したいのはここです。
この部分はDB設定が書かれています。
<!-- <env name="DB_CONNECTION" value="sqlite"/> -->
<!-- <env name="DB_DATABASE" value=":memory:"/> -->
本シリーズでは、DBはMariaDBをずっと使ってきましたが、テストをするときだけはこのSQLiteを使用するという設定です。
SQLiteは、軽量なDBでテストに適しています。
データをファイルに格納するファイルモードと、メモリ上に一時的に格納するインメモリモードの2種類があり、2行目は後者をオンにするという設定です。
もし興味があれば、比較している方がいらっしゃいますので良ければどうぞ。
https://qiita.com/ytkj/items/33dc4f1b2df542eff3c9
今回は、「テスト時はSQLiteを使用」し、「インメモリモードで実行」したいので先ほどの2行はコメントアウトを外してください!
「Ctrl」+ 「/」で簡単に解除できます!
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
2.2 Dockerfile修正
次に、SQLiteの実行環境がそもそもないので、Dockerfileを修正して再ビルドしましょう。
RUN apt-get update \
&& apt-get install -y zlib1g-dev mariadb-client vim libzip-dev nodejs npm libsqlite3-dev \
&& docker-php-ext-install zip pdo_mysql pdo_sqlite
実行コマンド
$ docker compose build php
$ docker compose up -d
ここまでできたら、いったんコミットしておきましょう。
実行コマンド
$ git add .
$ git commit -m "set up SQLite for the test DB"
コミットメッセージは自分が分かりやすければ何でも大丈夫です!
余談ですが、本シリーズのGit管理対象はsrc/だけでしたが、現場ではDockerfileなども管理したほうが良いと思います。(笑)
あと、設定ファイル(.envなど)には機密情報が書かれていることが多いので、あまりGitで管理しないほうが良いらしいです。
3. ファクトリー作成
次に、テスト用データを作成するために、Factoryを作成しましょう!
同じくテスト用データを作成できるものとしてシーダー(Seeder)がありますが、テストのたびに動的にデータを作成できるFactoryの方がテスト相性が良いです!
テストケースを作成するたびに作ればよいですが、最低限あったほうが良いなと思うものだけ今回先に作成してしまいます。
3.1 Factoryファイル作成
まずは、PHPのDockerコンテナ内に入ります。
実行コマンド
$ docker exec -it php-lab bash
artisanオプションで大学・学部・研究室のFactoryを作成しましょう。
実行コマンド
$ php artisan make:factory UniversityFactory
$ php artisan make:factory FacultyFactory
$ php artisan make:factory LabFactory
ファイルが作成出来たら中身を書いていきましょう。
大学ファクトリー
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\University>
*/
class UniversityFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->unique()->city() . '大学',
'type' => fake()->randomElement(['national', 'public', 'private']),
'created_by' => User::factory(),
];
}
}
学部ファクトリー
<?php
namespace Database\Factories;
use App\Models\University;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Faculty>
*/
class FacultyFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->unique()->word() . '学部',
'university_id' => University::factory(),
'created_by' => User::factory(),
];
}
}
研究室ファクトリー
<?php
namespace Database\Factories;
use App\Models\Faculty;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Lab>
*/
class LabFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->lastName() . '研究室',
'faculty_id' => Faculty::factory(),
'created_by' => User::factory(),
'description' => fake()->optional()->sentence(),
'url' => fake()->optional()->url(),
'professor_name' => fake()->optional()->name(),
'professor_url' => fake()->optional()->url(),
'gender_ratio_male' => $male = fake()->numberBetween(0, 100),
'gender_ratio_female' => 100 - $male,
];
}
}
city()とかword()とかは適当なので、他に何でもよいです。
レビューファクトリー(修正)
テスト用にシンプルにします。
<?php
namespace Database\Factories;
use App\Models\Lab;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Review>
*/
class ReviewFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'lab_id' => Lab::factory(),
'mentorship_style' => $this->faker->numberBetween(1, 5),
'lab_atmosphere' => $this->faker->numberBetween(1, 5),
'achievement_activity' => $this->faker->numberBetween(1, 5),
'constraint_level' => $this->faker->numberBetween(1, 5),
'facility_quality' => $this->faker->numberBetween(1, 5),
'work_style' => $this->faker->numberBetween(1, 5),
'student_balance' => $this->faker->numberBetween(1, 5),
];
}
}
3.2 モデル修正
各モデル側にもファクトリーが使えるように設定を追加しておきます。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory; // 追加
use Illuminate\Database\Eloquent\SoftDeletes;
class University extends Model
{
use HasFactory, SoftDeletes; // 追加
// ...
大学モデルの時はこんな感じです。
学部モデル・研究室モデルも全く同様に追加しておきましょう。
3.3 Tinkerで動作確認
では、正しく設定ができているかを確認しましょう。
以前このシリーズでも使ったことがあるTinkerを用いて確認します。
実行コマンド
$ php artisan tinker
> University::factory()->make();
> Faculty::factory()->make();
> Lab::factory()->make();
> Review::factory()->make();
例えば、大学ファクトリーの場合だと以下のような感じにできればOKです。

Tinkerを終了するには、「Ctrl」+「C」を入力すればよいのでしたね。
これにて、今日の内容はおしまいです!
コミットしましょう。
実行コマンド
$ git status
$ git commit -m "created factories to generate data for testing"
リモートリポジトリへのプッシュもしましょう。
実行コマンド
$ git push origin test/unit/db-set-up
GitHubを開いて、developブランチに対してプルリクエストを作成し、コンフリクトが起きていないことを確認したらマージしましょう。

今回使用したブランチはもう使わないので削除しましょう(「Delete branch」をクリック)。

4. まとめ・次回予告
ということで、今日からテストが始まりました!
今日はそのために、DBの設定やファクトリーの作成をしました。
次回から本格的にUnitテストを実施していきますので、お楽しみに!
これまでの記事一覧
☆要件定義・設計編
☆環境構築編
☆バックエンド実装編
- その7: バックエンド実装編① ~認証機能作成~
- その8: バックエンド実装編②前編 ~レビュー投稿機能作成~
- その8.5: バックエンド実装編②後編 ~レビュー投稿機能作成~
- その9: バックエンド実装編③ ~レビューCRUD機能作成~
- その10: バックエンド実装編④ ~レビューCRUD機能作成その2~
- その11: バックエンド実装編⑤ ~新規大学・学部・研究室作成機能作成~
- その12: バックエンド実装編⑥ ~大学検索機能作成~
- その13: バックエンド実装編⑦ ~大学・学部・研究室編集機能作成~
- その14: バックエンド実装編⑧ ~コメント投稿機能~
- その15: バックエンド実装編⑨ ~コメント編集・削除機能~
- その16: バックエンド実装編⑩ ~ブックマーク機能~
- その17: バックエンド実装編⑪ ~排他制御・トランザクション処理~
- その18: バックエンド実装編⑫ ~マイページ機能作成~
- その19: バックエンド実装編⑬ ~管理者アカウント機能作成~
- その20: バックエンド実装編⑭ ~通知機能作成~
- その21: バックエンド実装編⑮ ~ソーシャルログイン機能作成~
☆フロントエンド実装編
- その22: フロントエンド実装編① ~メインコンテンツ領域作成~
- その23: フロントエンド実装編② ~ヘッダー作成~
- その24: フロントエンド実装編③ ~サイドバー作成~
- その25: フロントエンド実装編④ ~ホームページ作成~
- その26: フロントエンド実装編⑤ ~大学検索結果画面作成~
- その27: フロントエンド実装編⑥ ~大学詳細・学部一覧画面作成~
- その28: フロントエンド実装編⑦ ~学部詳細・研究室一覧画面作成~
- その29: フロントエンド実装編⑧前編 ~研究室詳細・レビュー画面作成前編~
- その29.5: フロントエンド実装編⑧後編 ~研究室詳細・レビュー画面作成後編~
- その30: フロントエンド実装編⑨ ~マイページ作成~
- その31: フロントエンド実装編⑩ ~パンくずリスト作成~
- その32: フロントエンド実装編⑪ ~ログイン・新規登録モーダル作成~
- その33: フロントエンド実装編⑫ ~大学作成・編集モーダル作成~
- その34: フロントエンド実装編⑬ ~学部作成・編集モーダル作成~
- その35: フロントエンド実装編⑭ ~研究室作成・編集モーダル作成~
- その36: フロントエンド実装編⑮ ~レビュー作成・編集モーダル作成~
- その37: フロントエンド実装編⑯ ~コメント一覧表示モーダル作成~
- その38: フロントエンド実装編⑰ ~編集履歴ページ・削除依頼ページ・通知ドロップダウン作成~
- その39: フロントエンド実装編⑱ ~トースト作成~
- その40: フロントエンド実装編⑲ ~退会ページ作成~
- その41: フロントエンド実装編⑳ ~ファビコン作成~
- その42: フロントエンド実装編㉑ ~レスポンシブデザイン対応~
参考
軽く宣伝
YouTubeを始めました(というか始めてました)。
内容としては、Webエンジニアの生活や稼げるようになるまでの成長記録などを発信していく予定です。
現在、まったく再生されておらず、落ち込みそうなので、見てくださる方はぜひ高評価を教えもらえると励みになると思います。"(-""-)"