フラットphpをテストする


まえがき

phpunitを用いればphpのテストで困ることはありません。


ただし、今夜までだ!

テスト対象はフラットphp。classもvendorもなく、DBアクセスもありません。
もちろんcomposerも使用していません。

テスト対象のphp
<?php
namespace honya;

[$status, $res] = fncHonya();

header('HTTP/1.1 '.$status);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($res);

function fncHonya(){
  // ちょこっとビジネスロジック
  return [200, ['result'=>'ok']];
}

phpunitはフラットphpをテストできない!

初めて知りました。フラットphpで開発したことがなかったので。
テストコードを作る方が面倒でしたが、知恵を絞ってテストできるようにしてみました。

元のファイル構成:

app
└ honya.php

テストできるようにしたファイル構成:

app
└ honya.php
└ compser.json
└ phpunit.xml
└ tests
 └ HonyaTestClass.php
 └ HonyaTest.php
 └ fixtures

テスト用classを作る

HonyaTestClassを作成し、honya.phpをrequireしたメソッドを作成しました。

ポイントは3点

テスト用class
<?php
namespace honya;

class HonyaTestClass {

    private $header = [];
    private $http_status = null;
    private $body = null;

    /**
     * @runInSeparateProcess
     */
    public function request(){
        ob_start();
        require 'honya.php';
        $this->body = ob_get_clean();
        $this->http_status = http_response_code();
        $this->header = xdebug_get_headers(); //@TODO php7 xdebug
    }

    public function getHttpStatus(){ return $this->http_status; }
    public function getHeader(){ return $this->header; }
    public function getBody(){ return $this->body; }
}

namespace はテスト時に必要と分かりましたが、実際は不要です。
性能に影響がないので入れました。
echo で bodyを返すため、ob_start、ob_get_clean で echo 出力を奪い取っています。
@runInSeparateProcess を付けることでphpunitに邪魔されず、テスト対象の echo だけ取得できます。
header は header_list() で取得できないので(原因不明)、xdebug_get_headers() を使用しています。php5では機能しましたが、php7では機能しません(原因不明)

テストを作る

classさえ作ればテストコードは普通です。

HonyaTest.php(テスト本体)
<?php
namespace honya;

require_once 'tests\HonyaTestClass.php';
require_once 'tests\HonyaTestUtil.php';

class HonyaTest extends \PHPUnit\Framework\TestCase {

    use HonyaTestUtil;

    /** @runInSeparateProcess */
    public function test1() {

        $body = 'honya body';
        $this->init_function(200, $body);

        $_POST['honya_param1'] = 'honya1';
        $_SERVER['REQUEST_URI'] = '/honya';

        $clazz = new \honya\HonyaTestClass();
        $clazz->request();

        $this->assertEquals('honya2 body', $clazz->getBody());
        $this->assertEquals('200', $clazz->getHttpStatus());
//      $this->assertSame([], $clazz->getHeader()); //@TODO php7 xdebug
    }
}

テスト用utilを作る

ポイントは2点

  • namespace
  • trait
HonyaTestUtil.php()
<?php
namespace honya;

use phpmock\phpunit\PHPMock;

trait HonyaTestUtil {

    use PHPMock;

    public function init_function($http_status, $body){
        $this->getFunctionMock(__NAMESPACE__, 'getenv')->expects($this->any())->willReturn('');
    }
}

getFunctionMock は namespace が空のときエラーになります。
テスト対象(honya.php)に namespace を入れたのは、そういう訳です。

HonyaTestUtil はclassにしていましたが、new でも static でもエラーになるので、trait にしました。

./vendor/bin/phpunit

で、実行できます。
phpunit.xml のデフォルトで autoload.php と テスト対象フォルダを指定されてるためです。
カバレッジも表示できるみたいですが、表示するまでもないので、暇があったらやります。