非常教程

Phpunit 6参考手册

指南 | Guides

Extending 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.
Phpunit 6

PHPUnit 是一个 xUnit 的体系结构的 PHP 单元测试框架。

主页 https://phpunit.de/
源码 https://github.com/sebastianbergmann/phpunit
版本 6
发布版本 6.4

Phpunit 6目录

1.指南 | Guides
2.注释 | Annotations
3.声明 | Assertions