#
ドキュメント

Document

自分のための備忘録です。

PHPUnit

PHPUnit – The PHP Testing Framework

インストール

インストールする方法はいくつかあります。
Homebrewを使いインストールする方法とpharファイルをwget/curlでインストールする方法を記載します。

Homebrew

$ brew search phpunit | grep phpunit
// homebrew/php/phpunit
// homebrew/php/phpunit-skeleton-generator
// homebrew/php/phpunit@5.7
$ brew install phpunit

pharファイルをダウンロード

$ wget https://phar.phpunit.de/phpunit.phar
$ chmod +x phpunit.phar
$ sudo mv phpunit.phar /usr/local/bin/phpunit
$ phpunit --version

テスト実行コマンド

$ phpunit ExampleTest              // ワーキングディレクトリのExampleTest.phpを実行
$ phpunit -c phpunit.xml           // 設定ファイル指定
$ phpunit -c dir                   // 設定ファイルのあるディレクトリを指定
$ phpunit --group=hoge             // groupアノテーションを指定したテスト実行
$ phpunit --exclude-group=hoge     // 除外グループを指定して実行 
$ phpunit --debug                  // デバッグ情報を表示
$ phpunit --verbose                // 詳細な説明を表示
$ phpunit --coverage-html </path/to/directory>    // directoryへhtml形式のカバレッジ出力

configuration file

--configuration が設定 されていない場合は、現在の作業ディレクトリから phpunit.xml あるいは phpunit.xml.dist を (この順に) 探し、 見つかった場合はそれを自動的に読み込みます。

出典 PHPUnit マニュアル – 第5章 テストの構成

テストスイート

テストを作成するときには、テストを何らかの条件で分類しておくと便利です。この分類をテストスイートと呼びます。

「Symfony2 入門」(p250)

phpunit.xmlでテストスイートを定義できます。

<testsuites>
    <testsuite name="unit">
        <directory>/path/to/tests/unit</directory>
    </testsuite>
    <testsuite name="functional">
        <directory>/path/to/tests/functional</directory>
    </testsuite>
    <testsuite name="all">
        <directory>/path/to/tests</directory>
    </testsuite>
</testsuites>

テストスイートの実行。

$ phpunit --testsuite=unit
$ phpunit --testsuite=functional
$ phpunit --testsuite=all

カバレッジ

  • カバレッジを求めるには、XDebugを有効化
  • カバレッジに含めるファイルをphpunit.xmlで指定
  • PhpStormを使うときの追加設定は下記をリンクを参照
    http://www.findxfine.com/programming/php/995561767.htm
<filter>
  <whitelist>
    <directory suffix=".php">/path/to/dirctory</directory>
    <file>/path/to/file</file>
    <exclude>
      <directory suffix=".php">/path/to/directory</directory>
      <file>/path/to/file</file>
    </exclude>
  </whitelist>
</filter>

PHPUnit マニュアル – 付録C XML 設定ファイル

whitelistタグにprocessUncoveredFilesFromWhitelist属性を指定できますが、
指定したときにカバレッジを作成できないことがありました。

PHPUnit マニュアル – 第11章 コードカバレッジ解析

phpunit.xmlへログlを指定することで実行時にオプションの指定を省略できます。

<logging>
  <log type="coverage-html" target="/tmp/report" lowUpperBound="35"
       highLowerBound="70"/>
  <log type="coverage-clover" target="/tmp/coverage.xml"/>
  <log type="coverage-php" target="/tmp/coverage.serialized"/>
  <log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
  <log type="junit" target="/tmp/logfile.xml" logIncompleteSkipped="false"/>
  <log type="testdox-html" target="/tmp/testdox.html"/>
  <log type="testdox-text" target="/tmp/testdox.txt"/>
</logging>

setUpメソッド

PHPUnit は、準備用のコードの共有をサポートしています。 各テストメソッドが実行される前に、setUp() という名前のテンプレートメソッドが実行されます。setUp() は、テスト対象のオブジェクトを生成するような処理に使用します。 テストメソッドの実行が終了すると、それが成功したか否かにかかわらず、 tearDown() という名前の別のテンプレートメソッドが実行されます。 tearDown() では、テスト対象のオブジェクトの後始末などを行います。

PHPUnit マニュアル – 第4章 フィクスチャ

各メソッドが実行される前に」実行されます。クラスファイルで1回ではありません。

class SetupTest extends \PHPUnit_Framework_TestCase
{
    function setUp() {
        var_dump(date('Y-m-d H:i:s'));
    }

    /**
     * @test
     */
    function テスト1() {
        $this->assertTrue(true);
        
    }

    /**
     * @test
     */
    function テスト2() {
        $this->assertTrue(true);
        
    }
}

上記の例では2個メソッドがあるのでsetUpは2回実行されます。

例外テスト

アノテーション@expectedException(および関連するアノテーション)を使い例外をテストします。

<?php
class HelloWorld
{
    public function get($type = '')
    {
        if (strtolower($type) === 'exception') {
            throw new \Exception('例外発生。', '8');
        }

        return 'Hello World';
    }
<?php
require_once 'HelloWorld.php';
class HelloWorldTest extends \PHPUnit_Framework_TestCase
{
    /**
     * 例外テスト
     * 
     * @test
     * @expectedException \Exception
     * @expectedExceptionCode 8
     * @expectedExceptionMessage 例外発生
     */
    public function 例外のテスト() {
        $o = new HelloWorld();
        $o->get('exception');
    }
    
    /**
     * 正常テスト
     * @test
     */
    public function 正常テスト() {
        $o = new HelloWorld();
        $this->assertThat($o->get(), $this->equalTo('Hello World'));
    }
}

モック

クラス メソッド 戻り値型
\PHPUnit_Framework_TestCase getMockBuilder \PHP_Framework_MockObject_MockBuilder
\PHP_Framework_MockObject_MockBuilder getMock \PHP_Framework_MockObject_MockObject

モックを使った簡単なサンプルを記載します。

モックの対象となるクラス。

class Server
{
    public function execute() {
        $data = .........
        // 通信などの重い処理
        return $data;
    }
}

モックを使用してテストされるクラス(SUT)

class Client
{
    public function execute(Server $server) {
        $data = $server->execute();
        return 'Mock Test: ' . $data;
    }
}

モックを使用したテスト。

<?php
use appBundle\Service\Client;
use AppBundle\Service\Server;

class ClientTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @test
     */
    public function レスポンスが正しいこと()
    {
        $client = new Client();
        /**
         * @var \PHPUnit_Framework_MockObject_MockObject $mock
         */
        $mock = $this->getMockBuilder(Server::class)->getMock();
        $mock->expects($this->once())
            ->method('execute')
            ->willReturn('Hello World!');
        $response = $client->execute($mock);
        $this->assertThat($response, $this->equalTo('Mock Test: Hello World!');
    }
}
メソッド 内容
will('foo') モックからの返却する値
willReturn will($this->returnValue('foo'))

will($value)はwill($this->returnValue($value))と同じです。

例外を返す。

 // SomeClass クラスのスタブを作成します
 $stub = $this->createMock(SomeClass::class);

 // スタブの設定を行います
 $stub->method('doSomething')
   ->will($this->throwException(new Exception));

SUT

モックを使用してテストするクラス(上記例ではClient)をSUT(system under test)と呼ぶことがあります。

プライベートメソッド

基本はパブリックメソッドのテストでカバーしますが、
プライベートメソッドのテストが必要なときはリフレクションを使います。

テスト対象。

namespace \My\Package

class Example {
    private examplePrivateMethod($a, $b) {
    }
}

テスト。

<?php

use \My\Package\Example;

class ExampleTest extends PHPUnit_Framework_TestCase
{
    /**
     * @group relativePath
     */
    public function testExamplePrivateMethod()
        $obj    = new Example();
        $method = new \ReflectionMethod(get_class($obj), 'examplePrivateMethod');
        $method->setAccessible(true);
        $actual = $method->invoke($obj, '第1引数', '第2引数');  // invokeの第1引数は対象クラスのインスタンス
        $expect = '.....'
        $this->assertEquals($expect, $actual);
    }
}

プライベートなプロパティは下記記事を参考にしてください。
【PHP】リフレクションクラスでprivate変数、privateメソッドをテストする|リスティング広告の運用代行ならカルテットコミュニケーションズ

PhpStorm

PHPUnitの設定

preference > Languages & Frameworks > PHP > Test FrameWorks

PhpStorm設定

  • 図はPHPUnitをComposerでインストールしComposer autoloaderを指定しています。
  • Default Configuration Fileへ設定ファイル(通常phpunit.xmlやphpunit.dist.xml)を設定しないとカバレッジが正しく表示できません。

テスト実行

ただしく設定するとツールバーからテストを実行できます。 Run テストクラス width Coverage 図を実行すると下記のようにカバレッジが表示できます。

un テストクラス width Coverageの実行結果

その他

setUp()より前にdataProviderは実行されます。