hgsgtk / mpunit

Mini PHP xUnit Testing Framework

Home Page:https://speakerdeck.com/hgsgtk/self-made-xunit

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Initial Development Load Map

hgsgtk opened this issue · comments

初期開発における開発ロードマップを検討する

xUnit basic features

xUnit Test Patterns

  • Specify a test as a Test Method (page 348)
  • Specify the expected results within the test method in the form of calls to Assertion Methods (page 362)
  • Aggregate the tests into test suites that can be run as a single operation
  • Run one or more tests to get a report on the results of the test run

Test Discovery vs Test Enumeration

Test Discovery vs Test Enumeration
自分で意図的に登録していくのが後者、多くのxUnitファミリーはTest Discoveryを実装している

Test Selection - to run subsets of test methods based on some criteria
Test Suiteとは微妙に趣向が違うらしい
https://stackoverflow.com/questions/16707680/junit-test-methods-selection-at-runtime

References

xUnit

テスティングフレームワーク自作の事例

テスティングフレームワーク自作といえばうさみさん

PHPDocの扱い

dataProvider系のことをしようとしたらPHPDoc

step1

Specify a test as a Test Method (page 348)

Test Methodを認識して実行する

考えたいテストフレームワークの機能

  • テスト対象クラスを集める ex. 基底クラスを継承
  • テスト対象関数を特定・集める ex. testという接頭辞ではじまる関数
  • テスト結果をためる
  • 最後にたまったテスト結果を元にエラーレポートする

PHPUnitの構造

抽象クラス TestCase

  • abstract classである TestCase
  • TestCaseはAssertを継承しており、
  • SelfDescribing, Testという2つのインターフェースを実装している

(Interface) SelfDescribing

  • @internal This class is not covered by the backward compatibility promise for PHPUnit
    後方互換?

(Interface) Test

Test Interfaceはrun()というメソッドを持っている

/**
 * A Test can be run and collect its results.
 */
interface Test extends Countable
{
    /**
     * Runs a test and collects its result in a TestResult instance.
     */
    public function run(TestResult $result = null): TestResult;
}

このインターフェースを実装していることがテスト実行・結果の収集の条件

このインタフェースが継承しているのが、Countable Interface

(Interface) Countable

interface Countable {

    /**
     * Count elements of an object
     * @link https://php.net/manual/en/countable.count.php
     * @return int The custom count as an integer.
     * </p>
     * <p>
     * The return value is cast to an integer.
     * @since 5.1.0
     */
    public function count();
}

このインターフェースを実装していると、count()で結果が返ってくる

(Class) TestResult

また、このTestインターフェースは、TestResultを引数に持ち・戻り値として返す
このTestResultクラスは、テスト結果を持っている。

    /**
     * @var array
     */
    private $passed = [];

    /**
     * @var TestFailure[]
     */
    private $errors = [];

    /**
     * @var TestFailure[]
     */
    private $failures = [];

これらの結果に追加するメソッドとしてaddErroraddWarningなどが存在している

また、ゲッターとして、次のようなpublicインターフェースが用意されている

    public function failures(): array
    {
        return $this->failures;
    }

さらに、実際にテストケースを実行するrun()も存在

https://github.com/sebastianbergmann/phpunit/blob/3426cfda812934f0c72d3fdc4dceb92ca14686b2/src/Framework/TestResult.php#L609

かなり長いこれまでのpublicメソッドはほとんど使われている。別のインターフェースがTestResultに混じっているようなそんなクラスになっている印象

(Class) Assert

AssertにはassertXxxの機能がstaticメソッドで実装されている。継承関係なんだろうか、traitとかの類なきがするのはおれだけか?

(Class) Framework\Constaint\IsEmpty

IsEmptyなどそれぞれ別クラスで定義してる

matchesでboolで結果を返せる
failureDescriptionで失敗時の説明を文字列で渡している
ただし、外側から使われているのは、継承元の親クラスConstraint::evaluate()。このメソッドの中でmatchesが呼ばれる、形式的にはテンプレートメソッドパターンに近い方法。
Constraintクラスではこのmatchesは、例外などはでないがfalseが定型的に変えるよというものになっている。

PHPUnitのbin

composer.jsonでは、トップディレクトリのphpunitを示している

    "bin": [
        "phpunit"
    ],

https://github.com/sebastianbergmann/phpunit/blob/92d6f95d129638fcb48d3a260914a3f3f5660494/phpunit#L12

このbinでは、TextUI/Command::main()を実行する

https://github.com/sebastianbergmann/phpunit/blob/7e3e9eb699139eeb3a867c4a5a669f842df6f537/src/TextUI/Command.php#L158

mainは、コマンド引数とともに自身のrun()を実行

Command::run()

コマンド引数をまず解釈する
ここでTestRunnerクラスを生成する、

このTestRunnerは必要なテストを集めてきて(getTest())、それらを実行する(doRun()

PHPUnit

テスト結果をどう反映させているか

addXXXというメソッドがTestResultというクラスに存在してる。シグネチャは次の通り

    public function addError(Test $test, Throwable $t, float $time): void

この第一引数のTestクラスではCountableインターフェースを実装しており、つまり、count()でカウントできるものになっている。(アサーションの数をカウントできるようになっている?)

この処理の中では、IncompleteTestやSkippedTestといったinstanceかどうかを確認しており、その確認結果に基づいてような処理を変更している。

TestFailureクラスだった場合は、TestResult->errorの配列にTestInterfaceを実装したクラスが追加されていく。
(クラスにしよ)

Test Interfaceは次の通り

interface Test extends Countable
{
    /**
     * Runs a test and collects its result in a TestResult instance.
     */
    public function run(TestResult $result = null): TestResult;
}

Testはrun()だけもっていればいい。TestResultを渡して結果を記録してまたTestResultを返す。ふーん。