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
- https://ja.wikipedia.org/wiki/XUnit
- http://sunit.sourceforge.net/index.html
- The mother of all unit testing frameworks
テスティングフレームワーク自作の事例
テスティングフレームワーク自作といえばうさみさん
- PHPerKaigi 2018前夜祭でテスティングフレームワーク設計の話をします
- PHPerKaigi 2018 - 03/09 30分 PHPでテスティングフレームワークを実装する前に知っておきたい勘所 / うさみけんた
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 = [];
これらの結果に追加するメソッドとしてaddError
やaddWarning
などが存在している
また、ゲッターとして、次のようなpublicインターフェースが用意されている
public function failures(): array
{
return $this->failures;
}
さらに、実際にテストケースを実行するrun()
も存在
かなり長いこれまでの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"
],
このbinでは、TextUI/Command::main()
を実行する
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を返す。ふーん。