第 9 章 不完整的测试与跳过的测试

不完整的测试

开始写新的测试用例类时,可能想从写下空测试方法开始,比如:

public function testSomething()
{
}

以此来跟踪需要编写的测试。空测试的问题是 PHPUnit 框架会将它们解读为成功。这种错误解读导致错误报告变得毫无用处——无法分辨出测试是真的成功了还是根本就未编写实现。在未实现的测试中调用 $this->fail() 同样没啥帮助,因为测试将被解读为失败。这和将未实现的测试解读为成功是一样的错误。

假如把成功的测试视为绿灯、测试失败视为红灯,那么还额外需要黄灯来将测试标记为不完整或尚未实现。PHPUnit_Framework_IncompleteTest 是一个标记接口,用于将异常(由测试方法抛出)标记为测试不完整或目前尚未实现而导致的结果。PHPUnit_Framework_IncompleteTestError 是这个界面的标准实现。

例 9.1 展示了一个测试用例类 SampleTest,它有一个测试方法 testSomething()。通过在测试方法中调用便捷方法 markTestIncomplete()(会自动抛出一个 PHPUnit_Framework_IncompleteTestError 异常)将这个测试标记为不完整。

例 9.1: 将测试标记为不完整

<?php
class SampleTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        // 可选:如果愿意,在这里随便测试点什么。
        $this->assertTrue(TRUE, '这应该已经是能正常工作的。');

        // 在这里停止,并将此测试标记为不完整。
        $this->markTestIncomplete(
          '此测试目前尚未实现。'
        );
    }
}
?>


在 PHPUnit 命令行测试执行器的输出中,不完整的测试记为 I,如下例所示:

phpunit --verbose SampleTest
PHPUnit 3.7.0 by Sebastian Bergmann.

I

Time: 0 seconds, Memory: 3.75Mb

There was 1 incomplete test:

1) SampleTest::testSomething
This test has not been implemented yet.

/home/sb/SampleTest.php:12
OK, but incomplete or skipped tests!
Tests: 1, Assertions: 1, Incomplete: 1.

表 9.1 列举了用于将测试标记为不完整的 API。

表 9.1. 用于不完整的测试的 API

方法含义
void markTestIncomplete()将当前测试标记为不完整。
void markTestIncomplete(string $message)将当前测试标记为不完整,并用 $message 作为说明信息。


跳过测试

并非所有测试都能在任何环境中运行。比如说,考虑这样一种情况:一个数据库抽象层,针对其所支持的各种数据库系统有多个不同的驱动程序。针对 MySQL 驱动程序的测试当然只在 MySQL 服务器可用才运行。

例 9.2 展示了一个测试用例类 DatabaseTest,它有一个测试方法 testConnection()。在测试用例类的 setUp() 模板方法中,检查了 MySQLi 扩展是否可用,并且在扩展不可用时用 markTestSkipped() 方法来跳过此测试。

例 9.2: 跳过某个测试

<?php
class DatabaseTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        if (!extension_loaded('mysqli')) {
            $this->markTestSkipped(
              'MySQLi 扩展不可用。'
            );
        }
    }

    public function testConnection()
    {
        // ...
    }
}
?>


在 PHPUnit 命令行测试执行器的输出中,跳过的测试记为 S,如下例所示:

phpunit --verbose DatabaseTest
PHPUnit 3.7.0 by Sebastian Bergmann.

S

Time: 0 seconds, Memory: 3.75Mb

There was 1 skipped test:

1) DatabaseTest::testConnection
The MySQLi extension is not available.

/home/sb/DatabaseTest.php:9
OK, but incomplete or skipped tests!
Tests: 1, Assertions: 0, Skipped: 1.

表 9.2 列举了用于跳过测试的 API。

表 9.2. 用于跳过测试的 API

方法含义
void markTestSkipped()将当前测试标记为跳过。
void markTestSkipped(string $message)将当前测试标记为不完整,并用 $message 作为说明信息。


用 @requires 来跳过测试

除了上述方法,还可以用 @requires 标注来表达测试用例的一些常见前提条件。

表 9.3. 可能的 @requires 用法

类型可能的值范例其他范例
PHP任何 PHP 版本标识符@requires PHP 5.3.3@requires PHP 5.4-dev
PHPUnit任何 PHPUnit 版本标识符@requires PHPUnit 3.6.3@requires PHPUnit 3.7
function任何有效的 function_exists 参数@requires function imap_open@requires function ReflectionMethod::setAccessible
extension任何扩展名称@requires extension mysqli@requires extension curl


例 9.3: 用 @requires 来跳过测试

<?php
/**
 * @requires extension mysqli
 */
class DatabaseTest extends PHPUnit_Framework_TestCase
{
    /**
     * @requires PHP 5.3
     */
    public function testConnection()
    {
        // 测试要求有 mysqli 扩展,并且要求 PHP >= 5.3
    }

    // ... 所有其他需要 mysqli 扩展的测试
}
?>


如果在特定版本的 PHP 下使用了某种无法编译的语法,请在 XML 配置信息中查找包含在 “测试套件”一节 中的关于版本依赖的信息。

请在 GitHub 上 开启任务单 来对本页提出改进建议。万分感谢!