Extending PHPUnit
扩展PHPUnit
PHPUnit可以通过各种方式进行扩展,使测试的编写更加简单,并可以定制从运行测试中获得的反馈。以下是扩展PHPUnit的常见起点。
子类PHPUnit \ Framework \ TestCase
在抽象的子类中编写自定义断言和实用程序方法,PHPUnit\Framework\TestCase
并从该类派生您的测试用例类。这是扩展PHPUnit最简单的方法之一。
编写自定义断言
在编写自定义断言时,遵循PHPUnit自己的断言如何实现是最佳实践。正如你在例14.1中看到的,这个assertTrue()
方法只是一个包围isTrue()
和assertThat()
方法的包装器:isTrue()
创建一个传递给assertThat()
评估的匹配器对象。
例14.1:PHPUnit_Framework_Assert类的assertTrue()和isTrue()方法
<?php
use PHPUnit\Framework\TestCase;
abstract class PHPUnit_Framework_Assert
{
// ...
/**
* Asserts that a condition is true.
*
* @param boolean $condition
* @param string $message
* @throws PHPUnit_Framework_AssertionFailedError
*/
public static function assertTrue($condition, $message = '')
{
self::assertThat($condition, self::isTrue(), $message
}
// ...
/**
* Returns a PHPUnit_Framework_Constraint_IsTrue matcher object.
*
* @return PHPUnit_Framework_Constraint_IsTrue
* @since Method available since Release 3.3.0
*/
public static function isTrue()
{
return new PHPUnit_Framework_Constraint_IsTrue;
}
// ...
}?>
例14.2显示了如何PHPUnit_Framework_Constraint_IsTrue
扩展匹配器对象(或约束)的抽象基类,PHPUnit_Framework_Constraint
。
例14.2:PHPUnit_Framework_Constraint_IsTrue类
<?php
use PHPUnit\Framework\TestCase;
class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint
{
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other Value or object to evaluate.
* @return bool
*/
public function matches($other)
{
return $other === true;
}
/**
* Returns a string representation of the constraint.
*
* @return string
*/
public function toString()
{
return 'is true';
}
}?>
实施assertTrue()
和isTrue()
方法以及PHPUnit_Framework_Constraint_IsTrue
类的努力产生了assertThat()
自动处理评估断言和簿记任务的好处,例如统计数据。此外,该isTrue()
方法可以在配置模拟对象时用作匹配器。
实现PHPUnit_Framework_TestListener
例14.3给出了一个PHPUnit_Framework_TestListener
接口的简单实现。
例14.3:一个简单的测试监听器
<?php
use PHPUnit\Framework\TestCase;
class SimpleTestListener implements PHPUnit_Framework_TestListener
{
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Error while running test '%s'.\n", $test->getName()
}
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
{
printf("Test '%s' failed.\n", $test->getName()
}
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Test '%s' is incomplete.\n", $test->getName()
}
public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Test '%s' is deemed risky.\n", $test->getName()
}
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Test '%s' has been skipped.\n", $test->getName()
}
public function startTest(PHPUnit_Framework_Test $test)
{
printf("Test '%s' started.\n", $test->getName()
}
public function endTest(PHPUnit_Framework_Test $test, $time)
{
printf("Test '%s' ended.\n", $test->getName()
}
public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf("TestSuite '%s' started.\n", $suite->getName()
}
public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf("TestSuite '%s' ended.\n", $suite->getName()
}
}
?>
例14.4展示了如何对PHPUnit_Framework_BaseTestListener
抽象类进行子类化,从而使您只指定对您的用例感兴趣的接口方法,同时为所有其他类提供空实现。
例14.4:使用基本测试监听器
<?php
use PHPUnit\Framework\TestCase;
class ShortTestListener extends PHPUnit_Framework_BaseTestListener
{
public function endTest(PHPUnit_Framework_Test $test, $time)
{
printf("Test '%s' ended.\n", $test->getName()
}
}
?>
在“测试监听器”一节中,您可以看到如何配置PHPUnit以将测试监听器附加到测试执行。
子类PHPUnit_Extensions_TestDecorator
您可以将测试用例或测试套件包装在一个子类中,PHPUnit_Extensions_TestDecorator
并使用Decorator设计模式在测试运行之前和之后执行一些操作。
PHPUnit附带一个具体的测试装饰器:PHPUnit_Extensions_RepeatedTest
。它用于重复运行测试,并且只有在所有迭代都成功时才算作成功。
例14.5展示了一个PHPUnit_Extensions_RepeatedTest
测试装饰器的简化版本,它说明了如何编写你自己的测试装饰器。
例14.5:RepeatedTest装饰器
<?php
use PHPUnit\Framework\TestCase;
require_once 'PHPUnit/Extensions/TestDecorator.php';
class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
private $timesRepeat = 1;
public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1)
{
parent::__construct($test
if (is_integer($timesRepeat) &&
$timesRepeat >= 0) {
$this->timesRepeat = $timesRepeat;
}
}
public function count()
{
return $this->timesRepeat * $this->test->count(
}
public function run(PHPUnit_Framework_TestResult $result = null)
{
if ($result === null) {
$result = $this->createResult(
}
for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop( $i++) {
$this->test->run($result
}
return $result;
}
}
?>
实施PHPUnit_Framework_Test
该PHPUnit_Framework_Test
接口是有限且易于实现。例如,您可以编写PHPUnit_Framework_Test
比此更简单的实现,PHPUnit\Framework\TestCase
并运行数据驱动的测试
。
示例14.6显示了一个数据驱动的测试用例类,它将来自文件的值与逗号分隔值(CSV)进行比较。这样的文件的每一行看起来像foo;bar
,第一个值是我们期望的值,第二个值是实际值。
例14.6:数据驱动的测试
<?php
use PHPUnit\Framework\TestCase;
class DataDrivenTest implements PHPUnit_Framework_Test
{
private $lines;
public function __construct($dataFile)
{
$this->lines = file($dataFile
}
public function count()
{
return 1;
}
public function run(PHPUnit_Framework_TestResult $result = null)
{
if ($result === null) {
$result = new PHPUnit_Framework_TestResult;
}
foreach ($this->lines as $line) {
$result->startTest($this
PHP_Timer::start(
$stopTime = null;
list($expected, $actual) = explode(';', $line
try {
PHPUnit_Framework_Assert::assertEquals(
trim($expected), trim($actual)
}
catch (PHPUnit_Framework_AssertionFailedError $e) {
$stopTime = PHP_Timer::stop(
$result->addFailure($this, $e, $stopTime
}
catch (Exception $e) {
$stopTime = PHP_Timer::stop(
$result->addError($this, $e, $stopTime
}
if ($stopTime === null) {
$stopTime = PHP_Timer::stop(
}
$result->endTest($this, $stopTime
}
return $result;
}
}
$test = new DataDrivenTest('data_file.csv'
$result = PHPUnit_TextUI_TestRunner::run($test
?>
PHPUnit 6.4.0 by Sebastian Bergmann and contributors.
.F
Time: 0 seconds
There was 1 failure:
1) DataDrivenTest
Failed asserting that two strings are equal.
expected string <bar>
difference < x>
got string <baz>
/home/sb/DataDrivenTest.php:32
/home/sb/DataDrivenTest.php:53
FAILURES!
Tests: 2, Failures: 1.