1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

実務1年目駆け出しエンジニアがLaravel&ReactでWebアプリケーション開発に挑戦してみた!(テスト・デバッグ編①)~Unitテスト1[テストデータ準備・DB設定]~

1
Posted at

実務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ブランチにチェックアウトします。
実行コマンド

project-root/src
$ git checkout develop

前回使用したブランチはもう不要なので削除します(もし未削除なら)。
実行コマンド

project-root/src
$ git branch -D refactor/frontend/unnecessary-components

実行コマンド(確認用)

project-root/src
$ git branch

developブランチを最新化させます。
実行コマンド

project-root/src
$ git pull origin develop

新しいブランチを切って作業開始です!
実行コマンド

project-root/src
$ git checkout -b test/unit/db-set-up

※ブランチ名は、自分が分かりやすければ自由に変えてもらっても大丈夫です!

2. テスト用DB設定

2.1 phpunit.xml修正

srcディレクトリ直下にphpunit.xmlというファイルがあることを確認してください。

環境構築編でLaravelをインストールした時に自動で生成されているはずです。

testsuites

軽く中身を見てみましょう。

\project-root\src\phpunit.xml
    <testsuites>
        <testsuite name="Unit">
            <directory>tests/Unit</directory>    <!-- Unitテストの格納先 -->
        </testsuite>
        <testsuite name="Feature">
            <directory>tests/Feature</directory> <!-- Featureテストの格納先 -->
        </testsuite>
    </testsuites>

testsuitesにはUnitテストとFeatureテストの格納先が設定されています。

source

\app-lab-review\project-root\src\phpunit.xml
    <source>
        <include>
            <directory>app</directory> <!-- app/ 以下のコードがカバレッジ対象 -->
        </include>
    </source>

ここには、カバレッジ対象(テストの範囲)が設定されています。

php

環境変数(.envファイルの中身)が上書きされます。

今回修正したいのはここです。
この部分はDB設定が書かれています。

\project-root\src\phpunit.xml
        <!-- <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」+ 「/」で簡単に解除できます!

\project-root\src\phpunit.xml
        <env name="DB_CONNECTION" value="sqlite"/>
        <env name="DB_DATABASE" value=":memory:"/>

2.2 Dockerfile修正

次に、SQLiteの実行環境がそもそもないので、Dockerfileを修正して再ビルドしましょう。

\project-root\docker-config\php\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

実行コマンド

/project-root
$ docker compose build php
/project-root
$ docker compose up -d

ここまでできたら、いったんコミットしておきましょう。
実行コマンド

/project-root/src
$ git add .
/project-root/src
$ git commit -m "set up SQLite for the test DB"

コミットメッセージは自分が分かりやすければ何でも大丈夫です!

余談ですが、本シリーズのGit管理対象はsrc/だけでしたが、現場ではDockerfileなども管理したほうが良いと思います。(笑)

あと、設定ファイル(.envなど)には機密情報が書かれていることが多いので、あまりGitで管理しないほうが良いらしいです。

3. ファクトリー作成

次に、テスト用データを作成するために、Factoryを作成しましょう!

同じくテスト用データを作成できるものとしてシーダー(Seeder)がありますが、テストのたびに動的にデータを作成できるFactoryの方がテスト相性が良いです!

テストケースを作成するたびに作ればよいですが、最低限あったほうが良いなと思うものだけ今回先に作成してしまいます。

3.1 Factoryファイル作成

まずは、PHPのDockerコンテナ内に入ります。
実行コマンド

/project-root
$ docker exec -it php-lab bash

artisanオプションで大学・学部・研究室のFactoryを作成しましょう。
実行コマンド

/var/www
$ php artisan make:factory UniversityFactory
/var/www
$ php artisan make:factory FacultyFactory
/var/www
$ php artisan make:factory LabFactory

ファイルが作成出来たら中身を書いていきましょう。

大学ファクトリー

\project-root\src\database\factories\UniversityFactory.php
<?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(),
        ];
    }
}

学部ファクトリー

\project-root\src\database\factories\FacultyFactory.php
<?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(),
        ];
    }
}

研究室ファクトリー

\project-root\src\database\factories\LabFactory.php
<?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()とかは適当なので、他に何でもよいです。

レビューファクトリー(修正)

テスト用にシンプルにします。

\project-root\src\database\factories\ReviewFactory.php
<?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 モデル修正

各モデル側にもファクトリーが使えるように設定を追加しておきます。

\project-root\src\app\Models\University.php
<?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を用いて確認します。

実行コマンド

/var/www
$ php artisan tinker
Tinker
> University::factory()->make();
Tinker
> Faculty::factory()->make();
Tinker
> Lab::factory()->make();
Tinker
> Review::factory()->make();

例えば、大学ファクトリーの場合だと以下のような感じにできればOKです。
image.png

Tinkerを終了するには、「Ctrl」+「C」を入力すればよいのでしたね。

これにて、今日の内容はおしまいです!
コミットしましょう。

実行コマンド

/project-root/src
$ git status
/project-root/src
$ git commit -m "created factories to generate data for testing"

リモートリポジトリへのプッシュもしましょう。
実行コマンド

/project-root/src
$ git push origin test/unit/db-set-up

GitHubを開いて、developブランチに対してプルリクエストを作成し、コンフリクトが起きていないことを確認したらマージしましょう。
image.png

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

4. まとめ・次回予告

ということで、今日からテストが始まりました!
今日はそのために、DBの設定ファクトリーの作成をしました。

次回から本格的にUnitテストを実施していきますので、お楽しみに!

これまでの記事一覧

☆要件定義・設計編

☆環境構築編

☆バックエンド実装編

☆フロントエンド実装編

参考

軽く宣伝

YouTubeを始めました(というか始めてました)。
内容としては、Webエンジニアの生活や稼げるようになるまでの成長記録などを発信していく予定です。

現在、まったく再生されておらず、落ち込みそうなので、見てくださる方はぜひ高評価を教えもらえると励みになると思います。"(-""-)"

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?