CakePHPでデータベースのテストコードを実装する
最近、テストに関する本を読みました。
テスト駆動開発 | Kent Beck, 和田 卓人 |本 | 通販 | Amazon
読んで感じたのは、「テスト書かなきゃ!」という危機感です。
現在テストコードが皆無の開発案件(CakePHP)に携わっているのですが、これはどうにかしてテストを書こうと決めました。
特に、データベース関連のテストをずっと書きたいと思っていました。
(手動でテストするのが辛かったので・・・)
というわけで、今回はCakePHPでデータベースのテストを書く方法についてお伝えしていきます!
基本的に公式ドキュメントに沿っていますので、よければご参照ください。
CakePHPのバージョンは3を想定しています。
他のバージョンをお使いの場合は、適宜読み替えて下さい。
実装例
データベースアクセスをどのクラス(あるいは層)で行うかについては議論の余地があると思いますが、今回はテーブルクラスでORMを使ってデータベースにアクセスするという前提でテストを書いていきます。
では、以下のCompanyTable
のメソッドをテストしてみたいと思います。
実メソッド
CompanyTable.phpclass 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.phppublic 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
テストが成功すると、以下のような表示になります!
お疲れ様でした。
終わりに
実際にテストを書いてみると、本当に開発が楽になったと感じます!
自分が変更した箇所がきちんと動作しているという安心感があるので、リファクタリングもやりやすくなったと思います。
また、何か処理を追加するときに「どうテストするか」という視点で設計するようになったため、自然とテストしやすい(= 変更容易性の高い)コードがかけるようになりました。
みなさまもぜひテストを書きましょう!
それでは。