Engineering

Feb 17, 2017

CakePHPでテスト PHPUnitを使ったテスト

 

さて今回は PHPUnitを使ったテストです。

自分の運営しているサイトに修正が必要

だったので、 修正がてらテストコードも書いて見ました。

 

CakePHP で PHPUnitを使う。

 

CakePHPではPHPUnitの使用が想定されているので、 Composerで簡単に導入できます。

 

早速ですが、composerで依存性を追加

$ php composer.phar require --dev phpunit/phpunit:"<6.0"

 

Fixturesで使用するDBの設定

'test' => [
        'datasource' => 'Cake\Database\Driver\Mysql',
        'persistent' => false,
        'host' => 'dbhost',
        'username' => 'dblogin',
        'password' => 'dbpassword',
        'database' => 'test_database'
    ],

   

早速テストの実行

 

テストの実行は以下のコマンドで

vendor/bin/phpunit

 

 

まずはなにも書かず実行してみます。

$vendor/bin/phpunit
PHPUnit 5.7.13 by Sebastian Bergmann and contributors.

.IIIII....IIIIIII.                                                18 / 18 (100%)

Time: 506 ms, Memory: 15.25MB

OK, but incomplete, skipped, or risky tests!
Tests: 18, Assertions: 24, Incomplete: 12.

こうやって実行すると、 tests/TestCase の下のテストコードたちが 実行されます。

 

 

細かい規約は以下 CakePHPのCookBookから引用

  • テストを含むPHPファイルは、 tests/TestCase/[Type] ディレクトリに置きます。
  • ファイル名の最後は必ずただ .php とだけ書くのではなく Test.php とします。
  • テストを含むクラスは Cake\TestSuite\TestCaseCake\TestSuite\IntegrationTestCase または \PHPUnit_Framework_TestCase を継承する必要があります。
  • 他のクラス名と同様に、テストケースのクラス名はファイル名と一致する必要があります。 RouterTest.php は、 class RouterTest extends TestCase が含まれている 必要があります。
  • テストを含むメソッド (つまり、アサーションを含むメソッド) の名前は testPublished() のように test で始める必要があります。 @test というアノテーションをメソッドに マークすることでテストメソッドとすることもできます。

 

これだけではなにもわからないので、 実際にサイトで使われているコードのテストの一部を 書いて見ました。

 

 

テストをするのはCommonServiceの getLatestDateというメソッドで、

現在取得している株価の中で最新日付を 返してくれます。 具体的にいうと 今日が2017年の2月17日だとして、 17日の株価がまだ取得できていない時は、 2月の16日の日付を返してくれるというようなものです。 そしてもし、データ自体がない場合はNullを返します。

 

以下、実際のコードです

use Cake\TestSuite\IntegrationTestCase;
use App\Service\CommonService;
use Cake\ORM\TableRegistry;

/**
 * Created by PhpStorm.
 * User: version1
 * Date: 2017/02/17
 * Time: 20:26
 */
class RankingsServiceTest extends IntegrationTestCase
{
    private $common;
    private $kabukaTbl;
    public $fixtures = ['app.Commons/normal','app.Commons/empty'];

    function setUp()
    {
        $this->common = new CommonService();
        $this->kabukaTbl = TableRegistry::get('KabukaTbl');
    }

    /** getLatestDate is expected to return null
     *  when the result of select is empty.
     */
    function testGetLatestDate()
    {
        // load fixture
        $this->loadFixtures('Normal');
        $result = $this->common->getLatestDate();
        assert('2017-02-17', $result);
    }

    /** getLatestDate is expected to return null
     *  when the result of select is empty.
     */
    function testGetLatestDateError()
    {
        // load fixture
        $this->loadFixtures('Empty');
        $result = $this->common->getLatestDate();
        echo $result ;
        self::assertNull($result);
    }
}

コードはだいぶ端折っていて、 正常に日付が返るケースとデータがないケース のみのテストになります。

その中でもポイントはいくつかあって

  • 1.テスト前の準備メソッドsetUp()
  • 2.Fixtureの読み込み
の二つがポイントです。

 

 

テスト前の準備メソッドsetUp

テストクラスにsetUp()メソッドを実装すると クラス単位でのテストが始まる前に何らかの処理 例えば、テスト用のインスタンスの生成などを行えます。

また上では使っていませんが、 全テストの終了後の処理は、 tearDown()メソッドで実装できます。

 

 

 

Fixturesの読み込み

Fixtureとはテストを行う際に 自動でデータベースにあらかじめ用意したデータを挿入し、 テスト後に削除するという形になります。

コードを見るとわかるのですが、

public $fixtures = ['app.Commons/normal','app.Commons/empty'];

で使用するFixtureを選択し、

$this->loadFixtures('Normal');

のような形でテストケースごとに使用するFixtureを分けています。

また、 最初に読み込む時に 'app.commons' とすれば tests/Fixtures/CommonsFixture.phpのような Fixtures直下のものを読み込むことができます。

ただ、 1クラスにより複数種のデータを扱うテストがあることも考えると 一段Directoryを切って、 Fixturesをグループ化するということも大切です。

 

以上、自分のサイトのソースを使い、 CakePHPで テストコードを書いてみました。

実際のサイトにはここで書いた以上のテストを書く必要がありそうです。。

http://kabu-ka.net

 

私が去年から始めた株価データベースサイトです。 興味がおありでしたら覗いて見てください。。

関連記事

記事検索

気になるサイト内の記事を検索する

プロフィール

バンクーバー在住のフルスタックエンジニアです。React, Ruby on Rails, Go などでお仕事しています。職場がトロントなので日本、トロント、バンクーバーの三つの時天空を操って生活しています。

プロモーション