BookmarkLINELaravelで作成したアプリケーションをより信頼のおけるものとするために、テストは有用です。ユニットテストを行うにあたり、LaravelにはデフォルトでPHPUnitが使えるようになっています。今回は、LaravelとPHPUnitを用いて、アプリケーションの中でデータベース操作が絡む部分のユニットテストを作成します。開発環境は以下の通りです。7.5.9Laravelのルートディレクトリを「laravel/」とします。今回テストコードを書くにあたり、サンプルのアプリケーションを作成しています。同じ流れで試してみたい場合は以下より作成するか、Githubからソースをダウンロードしてください。TDDに則った手順ではなく、アサーションコード自体の細かい説明はあまり行いません。実際にCRUD操作を行うのでテスト用のスキーマを切って使用する事をおすすめします。テスト実行時のDB内容に左右されないようにテストコードを書く事で、より安定して自動テストを走らせることができます。DB作成まずはテスト用のDBを用意します。MySQLにログインして、テストに使用するスキーマを作成します。test_laravel_db;phpunit.xmlテスト用のスキーマを作成したら、それをphpunit.xmlに記述します。</php></phpunit>これでテストを実行した際にはテスト用のDBが使用されるようになりました。.env.testingテスト実行時はenvファイルの環境(APP_ENV)がtestingに切り替わります。その時に.env.testingが存在していればそちらの環境変数を見に行くので、テスト用envファイルを作成しておくと本番とは違う環境変数値を使いたい場合に便利です。ユニットテストここからユニットテストを書いていきます。インターフェイスのユニットテストまずはテストファイルを生成します。今回はRepositoryパターンでデータアクセス部分を定義したのでインターフェースのテストコード記述します。以下のartisanコマンドを叩いて、ユニットテス用のクラスを生成します。successfully.laravel/tests/Unit配下にMembersRepositoryInterfaceTest.phpが生成されます。MembersRepositoryInterfaceTest.phpこれを、定義されたメソッドについてテストコードを定義します。}}インターフェースのテストなので、抽象クラス(abstract)として定義しています。インターフェイスで宣言したメソッドについて、テストを定義しています。この時点ではまだデータベースの準備に関する事は何も定義していません。インターフェイス自体はそれ単体で動作するものではないので、テストクラスにはメソッドの規則としてのテストコードのみを記述しておきます。今回のサンプルアプリケーションには単純なメソッド(処理)のみが宣言・実装されているのでテストコードもシンプルなものになっています。各々のアプリケーションにテストを定義する場合はもちろん、要件に合ったものを定義します。派生クラスのユニットテスト次に派生クラスのテストコード書いていきます。以下のartisanコマンドを叩いて、ユニットテスト用のクラスを生成します。successfully.laravel/tests/Unit配下にMembersEloquentRepositoryTest.phpが生成されます。このクラスはインターフェイスのメソッドを実装しているのみで、独自のメソッドは定義していません。インターフェイスを実装したコードはインターフェイスのテストで評価されるので、ここでは記述する必要はありません。ここでは、データベースを使用してテストを行うその前準備と後始末を定義します。}}上から解説します。MembersRepositoryInterfaceTestインターフェイスのテストクラスを継承しています。することによって、派生クラスのテストを実行時にインターフェイス側で定義したテストも実行されます。RefreshDatabase;}RefreshDatabaseトレイトを使用しています。テスト開始時に1度だけマイグレーションを実行し、テーブルを作成してくれます。(データの投入は行いません。Members();}setUpメソッドを定義しています。これは、このテストクラスの各メソッドの開始前に必ず呼ばれるメソッドです。メソッドの数だけコールされます。親クラスのsetUpメソッドを実行(必須)Membersテーブルのシーディングをseed()メソッドで実行MembersRepositoryをインスタンス化ここで、Membersテーブルにシーダ経由でデータを投入しています。parent::tearDown();}tearDownメソッドを定義しています。これは、このテストクラスの各メソッドの終了後に必ず呼ばれるメソッドです。メソッドの数だけコールされます。データベースを初期状態(テーブル再構築&データ無し)に戻しています。親クラスのtearDownメソッドを実行(必須)テスト実行これで一通り定義したので、一度実行してみます。assertions)テストが実行され、全てが通る事が確認できました。毎回実行するDBの初期化について例えば今回のように、setUp()でシーディングを行い、tearDown()でDBを初期化する。これを毎回のメソッド毎に行われる事がとても無駄であると感じる事もあります。実際、もしこれがSELECTのテストのみであった場合、DB内のレコードは何も変更しないので、毎回作り直す必要はありません。テストは同じクラスであっても、その1つ1つが独立している必要があり、特定の場合を除いてはどこかのテストがどこかのテストの影響を受けるべきではありません。その考えと同じでDBも、どこかのテストがレコードに与えた変更で別のテストが失敗してしまうような構造は絶対に避けるべき点です。それを考えると、1回1回しっかりと初期化していくというのはテストを書く自由度やモチベーションを失わない為に必要だと思います。任意のデータを挿入するこれまでは、シーディングを実行してまとめてレコードを挿入していました。場合によっては任意のレコードを任意のタイミングで、任意の数挿入しテストを行いたい場合もあります。ここでは、シーディングではなく個別にテストDBにレコードを挿入してみます。FactoryクラスまずはFactoryクラスを作成します。以下のartisanコマンドを叩いて、Factoryクラスを生成します。successfully.laravel/database/factories配下にMemberFactory.phpが生成されます。MemberFactory.phpこれを例えば、以下のように定義します。'member_sub_3');全部で3つのレコードを定義しています。する事で、テストの中で任意のタイミングでこれらを挿入できます。実際に使用してみます。Members();}ここでは、setUpメソッドの中で2つのレコードを挿入しています。そのテストに応じて必要最小限のレコードを用意すれば良くなり、パフォーマンス向上に繋がります。Fakerによるダミーデータの挿入さっきの任意のレコードを挿入する場合もこれに含まれますが、シーダに定義するレコードというのは、アプリケーションをゼロから動かす場合に必要不可欠な値を予め定義しておく事がほとんどだと思います。記事やメッセージを扱うテーブルがあった場合に、それらのレコードをシーダに予め定義しておく事はしませんよね。いった性質のデータを扱う機能をテストしようとした時には、シーディングではない別の手段でテストデータを挿入してあげる必要があります。それにはもちろん、先に紹介した任意のレコードを定義して挿入する方法が有用です。データの定義がとても面倒であり、複数のレコードを全て定義していくのはとても骨が折れるし現実的ではありません。その場合には、ダミーデータを挿入してテストを行いましょう。LaravelにはFakerというダミーデータを生成するパッケージが導入されていて、テストでも気軽に使用できます。先に作成した、Factoryクラスを以下に変更します。];});Fakerがどんなダミーデータを生成できるのかは、オフィシャルを見ると確認できます。これをテストクラスで使用します。Members();}factoryメソッドの第二引数で、何個のレコードを挿入するかを指定しています。ここでは10件のダミーデータを挿入しています。ちなみに5件生成した場合はこんな感じに生成されました。))このように、幾つでも必要なだけデータを挿入できて、テスト時にも重宝します。ちなみにダミーデータを日本語にしたい場合はFakerで生成するダミーデータを日本語化するで方法を確認できます。まとめ以上で作業は完了です。安心感が得られるし、何よりそれで自動テストの実行時間も大幅に短縮されるはずです。今回のソースコードはGithubにアップしてあります。BookmarkLINELaravelとPHPUnit-データアクセスをモック化してHTTPテスト&ユニットテストを効率化する-LaravelのFakerとSeedingでダミーデータを自動生成しデータベースへ投入するLaravelでPHPUnitを使ったユニットテストを書く為のサンプルアプリケーション作成Laravel&CircleCIで継続的インテグレーション。ユニットテスト、静的コード解析、E2Eテストなど。SESを使ってAPIメール送信を行う(Mailableクラス&SimpleEmailService)2019.12.3MySQL8.0.16で追加されたCHECK制約を試してみる。RitoLaboこのサイトについて利用規約プライバシーポリシ。