CakePHPのテストコードを作成します。
参考:公式ページ
古いですが、CakePHP3、PHP7.3で試します。
PHPUnitとCakePHP独自のテスト機能があるみたいです。
インストール・環境構築
公式ドキュメントをご確認ください。
PHPUnitのインストールされてない方はインストールするくらいだと思います。
テストケース作成
以下のルールに従ってテストコードを記述するファイルを作成します。
試しにControllerのテストコードを作成します。
- テストを含むPHPファイルは、
tests/TestCase/[Type]ディレクトリーに格納- 今回、Controllerのテストコードを記述するので、
tests/TestCase/Controller配下に格納します。
- 今回、Controllerのテストコードを記述するので、
- ファイル名の最後は必ずTest.php とする。
- 以下のいずれかのクラスを継承する必要がある。
- Cake\TestSuite\TestCase
- Cake\TestSuite\IntegrationTestCase
- \PHPUnit\Framework\TestCase
- テストケースのクラス名はファイル名と一致する必要がある。
- テストを含むメソッド (つまり、アサーションを含むメソッド) は以下のいずれかのルールに従う必要がある。
- メソッド名を
testPublished()のようにtestで始める。 -
@testというアノテーションをメソッドに マークする。
- メソッド名を
<?php
namespace App\Test\TestCase\Controller\;
use App\Controller\<テスト対象クラス名>;
use Cake\ORM\Entity;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\IntegrationTestCase;
/**
* App\Controller\<テスト対象クラス名> Test Case
*/
class <テスト対象クラス名>Test extends IntegrationTestCase
{
/**
* Fixtures
*
* @var array
*/
public $fixtures = [
// 必要に応じてフィクスチャをロードしてください
];
private $table1;
private $ApiComponent;
private $AclComponent;
private $_eventListener;
/**
* setUp method
*
* テストケースクラスのメソッドが呼び出される前に毎回実行されます。
* 各オブジェクトの初期化やモックの設定を行います。
*/
public function setUp(): void
{
parent::setUp();
$mockTableData = [
new Entity(['key' => 'name', 'value' => '1']),
new Entity(['key' => 'kana', 'value' => '1']),
new Entity(['key' => 'tel', 'value' => '1']),
];
// --- テーブルのモック ---
$this->table1 = $this->getMockForModel('table1', ['deleteAll', 'saveMany', 'newEntities', '<その他独自のfunctionなど>']);
// functionがモックデータを返すように設定します
// これにより、コントローラがデータを取得しようとした際にエラーが発生するのを防ぎます
$this->table1->method('<返却値を設定したいfunction')->willReturn($mockTableData);
// コンポーネントのモックを作成します
$this->ApiComponent = $this->getMockBuilder('App\Controller\Component\ApiComponent')
->setMethods(['<mockで定義したいfunction>'])
->disableOriginalConstructor()
->getMock();
// アプリケーション固有のAclComponentをモックします。ビューで使われているメソッドも対象に含めます。
$this->AclComponent = $this->getMockBuilder('App\Controller\Component\AclComponent')
->disableOriginalConstructor()
->setMethods(['set', '<mockで定義したいfunction>'])
->getMock();
// AclComponentのメソッドが常にtrueを返すように設定します
// これにより、setScreenInfo()内の権限チェックでnullが返ってエラーになるのを防ぎます
$this->AclComponent->method('<mockで定義したいfunctionA>')->willReturn(true);
$this->AclComponent->method('<mockで定義したいfunctionB>')->willReturn('test');
// セッションのモック設定
$this->Session = $this->getMockBuilder('Cake\Controller\Component\SessionComponent')
->setMethods(['read', 'write', 'delete'])
->disableOriginalConstructor()
->getMock();
$this->Session->method('read')->willReturn(null);
$this->Session->method('write')->willReturn(null);
$this->Session->method('delete')->willReturn(null);
// Controller.startupイベントをフックするためのリスナーを定義します
$this->_eventListener = function (Event $event) {
$controller = $event->subject();
// コンポーネントを差し替えます
if ($controller instanceof <テスト対象クラス名>) {
$controller->Api = $this->ApiComponent;
$controller->Google = $this->GoogleComponent;
$controller->Acl = $this->AclComponent;
}
};
\Cake\Event\EventManager::instance()->on('Controller.startup', $this->_eventListener);
// 認証ユーザーとセッション、CSRF/Securityトークンを有効にします
〜〜〜〜
}
/**
* tearDown method
*
* テストケースクラスのメソッドが呼び出された後に毎回実行されます。
* 各オブジェクトのクリーンアップを行います。
*/
public function tearDown(): void
{
// グローバルなイベントリスナーを解除します
if ($this->_eventListener) {
\Cake\Event\EventManager::instance()->off('Controller.startup', $this->_eventListener);
$this->_eventListener = null;
}
TableRegistry::clear();
parent::tearDown();
}
/**
* indexアクション(GET)とビュー(index.ctp)のテスト
*/
public function testIndexGetAndRenderView(): void
{
// GETリクエストを送信
$this->get('<URLパス>/index');
// HTTPステータスコードが200系であることを確認
$this->assertResponseOk();
// 表示されているテンプレートが正しいことを確認
$this->assertTemplate('index');
// --- index.ctp の表示内容を検証 ---
// フォームの存在確認
$this->assertResponseContains('<form id="form1"');
// JavaScriptに変数が渡されていることを確認
$this->assertResponseRegExp('/var numeric = 1;/');
}
}ログ
最初なぜか標準出力のログだけを追ってデバッグしてて、難易度高いなと思ってたんですが、
普通にlogs配下にログが出てました。
実行
以下のコマンドを実行
vendor/bin/phpunit特定のファイルだけ実行したい場合
vendor/bin/phpunit <ファイル名> (tests/TestCase/〜)