Help us understand the problem. What is going on with this article?

phpunitで作成したテストケースをもとに試験項目表を作成する

目的

私が以前所属していたチームでは
1. 単体試験表を作成(EXCELなどのドキュメント)
2. ユニットテストの作成
3. 実装
4. テスト実施

という流れで開発を行っていましたが、単体試験の試験表を作成と保守していくコストが掛かるようになり

  1. ユニットコードの作成
  2. 試験項目表の自動生成
  3. 実装
  4. テスト実施

とすることはできないか検討してみました。

手順

phpunit.xmlで試験の結果をjunit形式で出力する

phpunitのマニュアルによるとphpunit.xmlにtype="junit"を指定するとテスト実行時に自動でログを出力してくれるので追記します。
今回はtests配下にlogというフォルダを設けてそちらに出力します。

phpunit.xml
    <logging>
        <log type="junit" target="./tests/log/logfile.xml"/>
    </logging>

試しに実行してみます

vendor/bin/phpunit
PHPUnit 7.5.13 by Sebastian Bergmann and contributors.

...                                                                 3 / 3 (100%)

Time: 1.33 seconds, Memory: 14.00 MB
logfile.xml
<testsuites>
<testsuite name="" tests="3" assertions="3" errors="0" failures="0" skipped="0" time="1.102644">
<testsuite name="Unit" tests="2" assertions="2" errors="0" failures="0" skipped="0" time="0.806409">
<testsuite name="Tests\Unit\Auth\RegisterControllerTest" file="/var/www/html/tests/Unit/Auth/RegisterControllerTest.php" tests="1" assertions="1" errors="0" failures="0" skipped="0" time="0.768928">
<testcase name="testExample" class="Tests\Unit\Auth\RegisterControllerTest" classname="Tests.Unit.Auth.RegisterControllerTest" file="/var/www/html/tests/Unit/Auth/RegisterControllerTest.php" line="16" assertions="1" time="0.768928"/>
</testsuite>
<testsuite name="Tests\Unit\ExampleTest" file="/var/www/html/tests/Unit/ExampleTest.php" tests="1" assertions="1" errors="0" failures="0" skipped="0" time="0.037481">
<testcase name="testBasicTest" class="Tests\Unit\ExampleTest" classname="Tests.Unit.ExampleTest" file="/var/www/html/tests/Unit/ExampleTest.php" line="15" assertions="1" time="0.037481"/>
</testsuite>
</testsuite>
<testsuite name="Feature" tests="1" assertions="1" errors="0" failures="0" skipped="0" time="0.296235">
<testsuite name="Tests\Feature\ExampleTest" file="/var/www/html/tests/Feature/ExampleTest.php" tests="1" assertions="1" errors="0" failures="0" skipped="0" time="0.296235">
<testcase name="testBasicTest" class="Tests\Feature\ExampleTest" classname="Tests.Feature.ExampleTest" file="/var/www/html/tests/Feature/ExampleTest.php" line="15" assertions="1" time="0.296235"/>
</testsuite>
</testsuite>
</testsuite>
</testsuites>

...かなりわかりにくいですがひとまず試験の内容を出力することができました。

見やすい形式に変換する

こちらのXMLTテンプレートをもとにXMLをHTMLに変換するとかなり見やすくなるようですので、変換してみます。

※xsltprocが入っていない場合は以下のコマンドでインストールしてください。

sudo apt-get install xsltproc

こちらがHTML変換用のコマンドです。

xsltproc phpunit.xslt tests/log/logfile.xml > output.html

こちらが出力されたHTMLです、かなり見やすくなったのではないでしょうか。
output_html.png

ただ、そのまま使うだけだとテストケースが出力されないため試験の内容がわかりにくく感じたのでテンプレートの内容を少し修正しました。
2019-08-02_13h03_32.png

修正したphpunit.xsltはこちらにも配置してあるので好きに使ってください。
https://gist.github.com/yamamoto-taku/aa3cf2fe6d498a5a855c0f948a29cecd

実例

sampleとしてlaravelの会員登録フォームのバリデーションについてユニットテストを作成し、どのような感じになるか試してみます。

まず以下のコマンドでlaravelの会員機能一式を作成します。

php artisan make:auth

上記のコマンドで作成された以下の関数を試験するテストコードを作成しようと思います。

RegisterController.php
    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            //デフォルトのバリデーションだとひらがなでも登録できるので末尾の正規表現は追記しています。
            'name' => ['required', 'string', 'max:255', 'regex:/^[a-zA-Z0-9-_]+$/'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }
RegisterControllerTest.php
<?php

namespace Tests\Unit\Auth;

use App\Http\Controllers\Auth\RegisterController;
use Tests\TestCase;

class RegisterControllerTest extends TestCase
{
    protected $target;

    /**
     * @return void
     */
    public function setUp(): void
    {
        parent::setUp();
        $this->target = new RegisterController();
    }

    /**
     * @test
     * @throws \ReflectionException
     */
    public function 正常系()
    {
        $method = $this->changeAccessible('validator');

        $input = [
            'name' => 'yamamoto-taku',
            'email' => 'hoge@example.com',
            'password' => 'password',
            'password_confirmation' => 'password',
        ];
        $validator = $method->invoke($this->target, $input);
        $this->assertTrue($validator->passes());
    }

    /**
     * @param array $input
     * @dataProvider validatorProviderForFailure
     * @test
     * @throws \ReflectionException
     */
    public function 異常系(array $input)
    {
        $method = $this->changeAccessible('validator');
        $validator = $method->invoke($this->target, $input);
        $this->assertFalse($validator->passes());
    }

    /**
     * @return array
     */
    public function validatorProviderForFailure()
    {
        $input = [
            'name' => 'あいうえお',
            'email' => 'hoge@example.com',
            'password' => 'password',
            'password_confirmation' => 'password',
        ];
        $null = [
            'name' => null,
            'email' => null,
            'password' => null,
            'password_confirmation' => null,
        ];

        return [
            '必須入力チェック' => [$null],
            '日本語入力ひらがな' => [$input],
        ];
    }


    /**
     * @param $object
     * @return mixed
     * @throws \ReflectionException
     */
    public function changeAccessible($methodName)
    {
        $reflection = new \ReflectionClass($this->target);
        $method = $reflection->getMethod($methodName);
        $method->setAccessible(true);
        return $method;
    }
}

phpunitを実行してHTMLへの変換処理をかけます。

vendor/bin/phpunit tests/Unit/Auth/RegisterControllerTest.php
xsltproc phpunit.xslt tests/log/logfile.xml > output.html

生成された結果がこちらです。
データセットを持つ試験項目はデータラベルで、単一の試験項目については関数名でレビュアーにどのような試験を実施する想定かを伝えることができそうです!
2019-08-02_15h32_51.png

終わりに

上記のような自動レポートの機能を使って単体試験表を別途書かなくてもOKとなればいいなーと思います。あと変換の処理も合わせて自動でできそうな気もするのでそちらも別途検討してみようと思います。
PHPUnitのテストランナーを拡張して自動化できましたのでつづきを書きました

参考

PHPUnit のテスト結果を人間に優しい感じで出力する

自動化対象のユニットテスト(単体テスト)の仕様書を書くことは完全なる無駄である

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away