ここ数年仕事ではPHPを使って開発をしていますが、最近品質について考える機会が増えたこともあり、これを機にPHPUnitと周辺のモジュールの仕組みを理解してより楽にテストができるようにしたいと思います。
PHPUnitは?
Created PHPUnit. Co-Founded thePHP.cc. Helps PHP developers build better software.
PHPUnitの作者は、Sebastian Bergmannという方でthePHP.ccのファウンダーのようです。関連情報は以下にて確認してみてください。
PHPUnitの構成
PHPUnitは以下のような構成になっています。
- phpunit
- php-code-coverage
- php-file-iterator
- php-text-template
- php-timer
- php-token-stream
- phpunit
- phpunit-mock-objects
これらはGitHub上ではそれぞれ別々のリポジトリに分かれていますが、phpunitが本体でそれ以外はデフォルトの関連ライブラリという位置づけになるかと思います。
PHPUnitのsrc構成
モックオブジェクトなどを除いたphpunitだけのパッケージとクラス構成を見てみるとこんな感じになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
<code>├── Exception.php ├── Extensions │ ├── GroupTestSuite.php │ ├── PhptTestCase.php │ ├── PhptTestSuite.php │ ├── RepeatedTest.php │ ├── TestDecorator.php │ └── TicketListener.php ├── ForwardCompatibility │ └── TestCase.php ├── Framework │ ├── Assert │ │ └── Functions.php │ ├── Assert.php │ ├── AssertionFailedError.php │ ├── BaseTestListener.php │ ├── CodeCoverageException.php │ ├── Constraint │ │ ├── And.php │ │ ├── ArrayHasKey.php │ │ ├── ArraySubset.php │ │ ├── Attribute.php │ │ ├── Callback.php │ │ ├── ClassHasAttribute.php │ │ ├── ClassHasStaticAttribute.php │ │ ├── Composite.php │ │ ├── Count.php │ │ ├── Exception.php │ │ ├── ExceptionCode.php │ │ ├── ExceptionMessage.php │ │ ├── ExceptionMessageRegExp.php │ │ ├── FileExists.php │ │ ├── GreaterThan.php │ │ ├── IsAnything.php │ │ ├── IsEmpty.php │ │ ├── IsEqual.php │ │ ├── IsFalse.php │ │ ├── IsFinite.php │ │ ├── IsIdentical.php │ │ ├── IsInfinite.php │ │ ├── IsInstanceOf.php │ │ ├── IsJson.php │ │ ├── IsNan.php │ │ ├── IsNull.php │ │ ├── IsTrue.php │ │ ├── IsType.php │ │ ├── JsonMatches │ │ │ └── ErrorMessageProvider.php │ │ ├── JsonMatches.php │ │ ├── LessThan.php │ │ ├── Not.php │ │ ├── ObjectHasAttribute.php │ │ ├── Or.php │ │ ├── PCREMatch.php │ │ ├── SameSize.php │ │ ├── StringContains.php │ │ ├── StringEndsWith.php │ │ ├── StringMatches.php │ │ ├── StringStartsWith.php │ │ ├── TraversableContains.php │ │ ├── TraversableContainsOnly.php │ │ └── Xor.php │ ├── Constraint.php │ ├── Error │ │ ├── Deprecated.php │ │ ├── Notice.php │ │ └── Warning.php │ ├── Error.php │ ├── Exception.php │ ├── ExceptionWrapper.php │ ├── ExpectationFailedException.php │ ├── IncompleteTest.php │ ├── IncompleteTestCase.php │ ├── IncompleteTestError.php │ ├── InvalidCoversTargetException.php │ ├── OutputError.php │ ├── PHPUnit_Framework_CoveredCodeNotExecutedException.php │ ├── PHPUnit_Framework_MissingCoversAnnotationException.php │ ├── RiskyTest.php │ ├── RiskyTestError.php │ ├── SelfDescribing.php │ ├── SkippedTest.php │ ├── SkippedTestCase.php │ ├── SkippedTestError.php │ ├── SkippedTestSuiteError.php │ ├── SyntheticError.php │ ├── Test.php │ ├── TestCase.php │ ├── TestFailure.php │ ├── TestListener.php │ ├── TestResult.php │ ├── TestSuite │ │ └── DataProvider.php │ ├── TestSuite.php │ ├── UnintentionallyCoveredCodeError.php │ ├── Warning.php │ └── WarningTestCase.php ├── Runner │ ├── BaseTestRunner.php │ ├── Exception.php │ ├── Filter │ │ ├── Factory.php │ │ ├── Group │ │ │ ├── Exclude.php │ │ │ └── Include.php │ │ ├── Group.php │ │ └── Test.php │ ├── StandardTestSuiteLoader.php │ ├── TestSuiteLoader.php │ └── Version.php ├── TextUI │ ├── Command.php │ ├── ResultPrinter.php │ └── TestRunner.php └── Util ├── Blacklist.php ├── Configuration.php ├── ConfigurationGenerator.php ├── ErrorHandler.php ├── Fileloader.php ├── Filesystem.php ├── Filter.php ├── Getopt.php ├── GlobalState.php ├── InvalidArgumentHelper.php ├── Log │ ├── JSON.php │ ├── JUnit.php │ ├── TAP.php │ └── TeamCity.php ├── PHP │ ├── Default.php │ ├── Template │ │ └── TestCaseMethod.tpl.dist │ ├── Windows.php │ └── eval-stdin.php ├── PHP.php ├── Printer.php ├── Regex.php ├── String.php ├── Test.php ├── TestDox │ ├── NamePrettifier.php │ ├── ResultPrinter │ │ ├── HTML.php │ │ ├── Text.php │ │ └── XML.php │ └── ResultPrinter.php ├── TestSuiteIterator.php ├── Type.php └── XML.php </code> |
PHPUnitのメイン処理
PHPUnitでテストを実行するときのメイン処理をざくっと見てみます。
PHPUnit_TextUI_Command
PHPUnit_TextUI_TestRunner
TextUIのCommandとTestRunnerはphpunitのエントリポイントでphpunit実行時CommandからTestRunnerを呼び出し処理を開始します。
PHPUnit_Framework_Test(interface)
PHPUnit_Framework_TestSuite
TestRunnerはTestSuiteを作成しTestSuiteのメイン処理であるrun処理を実行します。
PHPUnit_Framework_Test(interface)
PHPUnit_Framework_Assert
PHPUnit_Framework_TestCase
PHPUnit_Framework_TestResult
TestSuiteはforeachにて順次TestCaseのテストを実行します。また、TestCaseの親クラスにはAssertがいてアサーションができるようになっています。
テストを実行する該当ソースをチェックする
TestSuite.php
TestRunnerから呼び出されたTestSuiteは、run処理にて対象テストをforeachして順次テストを実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
foreach ($this as $test) { if ($result->shouldStop()) { break; } if ($test instanceof PHPUnit_Framework_TestCase || $test instanceof self) { $test->setbeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); $test->setBackupGlobals($this->backupGlobals); $test->setBackupStaticAttributes($this->backupStaticAttributes); $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); } $test->run($result); } |
TestCase.php
TestCaseのrun処理ではTestResultのrun処理を実行します。
1 2 3 4 5 6 7 8 9 |
if ($result === null) { $result = $this->createResult(); } ...省略... $result->run($this); |
TestResult.php
TestResultのrun処理では、渡されたTestCaseのrunBare()を実行します。
1 2 3 |
$test->runBare(); |
TestCase.php
TestCaseのrunBare処理がテスト実行の最終地点で、before・after等のhookポイントを使いsetUp、テスト、tearDownを実行します。TestResultから呼び出しているのはテスト結果をTestResultが処理する為でしょう。実行後は順次TestResultが処理の呼び出し元に順に返され処理が終了します。
1 2 3 4 5 6 7 8 9 10 |
foreach ($hookMethods['before'] as $method) { $this->$method(); } $this->assertPreConditions(); $this->testResult = $this->runTest(); $this->verifyMockObjects(); $this->assertPostConditions(); |
かなりざっくりではありますがphpunitのメイン処理を確認してみました。だいたいの処理の流れは分かったので次回はモックオブジェクトを見ていこうと思います。