バグ解消法、お役立ち情報など

CakePHPでデータベースのテストコードを実装する

CakePHP logo

最近、テストに関する本を読みました。

テスト駆動開発 | Kent Beck, 和田 卓人 |本 | 通販 | Amazon

単体テストの考え方/使い方

読んで感じたのは、「テスト書かなきゃ!」という危機感です。

現在テストコードが皆無の開発案件(CakePHP)に携わっているのですが、これはどうにかしてテストを書こうと決めました。

特に、データベース関連のテストをずっと書きたいと思っていました。

(手動でテストするのが辛かったので・・・)

というわけで、今回はCakePHPでデータベースのテストを書く方法についてお伝えしていきます!

基本的に公式ドキュメントに沿っていますので、よければご参照ください。

テスト - 3.10

CakePHPのバージョンは3を想定しています。

他のバージョンをお使いの場合は、適宜読み替えて下さい。

実装例

データベースアクセスをどのクラス(あるいは層)で行うかについては議論の余地があると思いますが、今回はテーブルクラスでORMを使ってデータベースにアクセスするという前提でテストを書いていきます。

では、以下のCompanyTableのメソッドをテストしてみたいと思います。

実メソッド

CompanyTable.php
class CompanyTable extends Table { public function getCompanyByCompanyId(string $companyId): Company { return $this->find()->where(['company_id' => $companyId])->first(); } }

getCompanyByCompanyIdというパブリックメソッドがあります。

引数で$companyIdを受け取り、Companyモデルのエンティティを返します。

(記述をシンプルにするため、useなどは省略しています)

このメソッドをテストしてみましょう!

テストクラス

テストクラスの作成は、以下のbakeコマンドで行います。

bin/cake bake test table <table>

今回のCompanyテーブルの場合、以下のコマンドになります。

bin/cake bake test table companies

(データベースのテーブル名は複数形になります)

すると、以下のようなテストクラスが自動で作成されます。

path/to/tests/TestCase/Model/Table/CompanyTableTest.php
<?php namespace App\Test\TestCase\Model\Table; use App\Model\Table\CompanyTable; use Cake\ORM\TableRegistry; use Cake\TestSuite\TestCase; use App\Model\Entity\Company; class CompanyTableTest extends TestCase { /** * @var \App\Model\Table\CompanyTable */ public $Company; public $fixtures = [ 'app.Company', // Fixtureクラスを指定。 ]; public function setUp() { parent::setUp(); $config = TableRegistry::getTableLocator()->exists('companies') ? [] : ['className' => CompanyTable::class]; $this->Company = TableRegistry::getTableLocator()->get('companies', $config); } public function tearDown() { unset($this->Company); parent::tearDown(); }

同時にCompaniesFixtureクラスも作成されたかと思いますが、こちらは後ほど設定していきますので、現時点ではそのままにしておいて下さい。

コードの内容については後ほど説明するとして、ここからは実際にテストを書いていきましょう!

先ほど作成したCompanyTableTestクラスに追記していきます。

CompanyTableTest.php
public function testGetCompanyByIdAndCompanyId() { $result = $this->Company->getCompanyByCompanyId( 'company_id_1', ); $this->assertSame('company_id_1', $result->companyId); }

テストの内容は以下のとおりです。

getCompanyByCompanyIdメソッドに'company_id_1'を渡すと、'company_id_1'のレコードが返ってくること

アサート関連メソッドは色々な種類がありますので、よければPHPUnitの公式ドキュメントもご参照ください!

1. Assertions — PHPUnit 10.1 Manual

まだデータベースとの接続ができないはずなので、テストは失敗します。

ここから、データベースへとアクセスする処理を書いていきます!

では、改めてCompaniesFixtureクラスを見てみましょう。

フィクスチャとは、テスト専用のデータベースを使ってテストを行うためのクラスです。

こんな感じになっていると思います。

path/to/tests/Fixture/CompaniesFixture.php
<?php namespace App\Test\Fixture; use Cake\TestSuite\Fixture\TestFixture; class CompaniesFixture extends TestFixture { public $fields = [ '省略', ]; public function init() { $this->records = [ [ '省略', ], ]; } }

以下のように設定していきます。

  • $importプロパティにテーブル名を指定
  • initメソッド内に、テストで使用するレコードを指定

具体的には以下のとおりです。

path/to/tests/Fixture/CompaniesFixture.php
<?php namespace App\Test\Fixture; use Cake\TestSuite\Fixture\TestFixture; class CompaniesFixture extends TestFixture { public $fields = [ '省略', ]; // ↓追記 public $import = ['table' => 'companies']; public function init() { $this->records = [ [ // ↓追記 'company_id'=>'company_id_1', ], ]; } }

このように指定することで、companiesテーブルにcompany_id = company_id_1のレコードを自動で作成し、テストを実施してくれます。

便利なのは、レコードはテスト中のみ存在し、テストが終了すると消えてくれるところです。

なので、データベースのデータを用意する必要はありません!

めちゃくちゃ楽です!

なお、Fixtureを使用するには(デフォルトでは)testというデータベースと、それぞれのテーブルが必要です。

もしtestデータベースが存在しない場合は作成しておきましょう。

(詳細は公式ドキュメントをご参照ください)

では、いよいよテストを実行してみましょう!

テスト実行

以下のコマンドでテストを実行します。

vendor/bin/phpunit

テストが成功すると、以下のような表示になります!

お疲れ様でした。

終わりに

実際にテストを書いてみると、本当に開発が楽になったと感じます!

自分が変更した箇所がきちんと動作しているという安心感があるので、リファクタリングもやりやすくなったと思います。

また、何か処理を追加するときに「どうテストするか」という視点で設計するようになったため、自然とテストしやすい(= 変更容易性の高い)コードがかけるようになりました。

みなさまもぜひテストを書きましょう!

それでは。

バグ解消法、お役立ち情報など