附录 B. 标注

所谓标注,是指某些编程语言中允许加在源代码中的一种特殊形式的语法元数据。PHP 并没有专门的语言特性来支持对源代码进行标注,然而 PHP 社区早已经形成惯例,通过在文档注释块中使用诸如 @annotation arguments 这样的标签来对标注源代码。在 PHP 中,文档注释块是可反射的:可以通过在函数、方法、类以及属性级别调用反射 API 的 getDocComment() 方法来访问它们。诸如 PHPUnit 这样的应用程序在运行时用这些信息来配置其行为。

本附录列出了 PHPUnit 所支持的所有标注种类。

@author

@author 标注是 @group 标注(参见 “@group”一节)的别名,允许基于作者对测试进行过滤。

@after

@after 标注用于指定若干方法,这些方法在测试用例类中的每个测试方法运行完成之后调用。

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @after
     */
    public function tearDownSomeFixtures()
    {
        // ...
    }

    /**
     * @after
     */
    public function tearDownSomeOtherFixtures()
    {
        // ...
    }
}

@afterClass

@afterClass 标注用于指定若干静态方法,这些方法在测试类中的所有测试方法都运行完成之后调用,用来清理共享基境。

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @afterClass
     */
    public static function tearDownSomeSharedFixtures()
    {
        // ...
    }

    /**
     * @afterClass
     */
    public static function tearDownSomeOtherSharedFixtures()
    {
        // ...
    }
}

@backupGlobals

全局变量的备份与还原操作可以对测试用例类中的所有测试彻底禁用,像这样:

/**
 * @backupGlobals disabled
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    // ...
}

@backupGlobals 标注也可以用在测试方法这一级别。这样可以对备份与还原操作进行更细粒度的配置:

/**
 * @backupGlobals disabled
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @backupGlobals enabled
     */
    public function testThatInteractsWithGlobalVariables()
    {
        // ...
    }
}

@backupStaticAttributes

类的静态属性的备份与还原操作可以对测试用例类的所有测试彻底禁用,像这样:

/**
 * @backupStaticAttributes disabled
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    // ...
}

@backupStaticAttributes 标注也可以用在测试方法这一级别。这样可以对备份与还原操作进行更细粒度的配置:

/**
 * @backupStaticAttributes disabled
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @backupStaticAttributes enabled
     */
    public function testThatInteractsWithStaticAttributes()
    {
        // ...
    }
}

@before

The @before 标注用于指定若干方法,这些方法在测试用例类中的每个测试方法运行之前调用。

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @before
     */
    public function setupSomeFixtures()
    {
        // ...
    }

    /**
     * @before
     */
    public function setupSomeOtherFixtures()
    {
        // ...
    }
}

@beforeClass

@beforeClass 标注用于指定若干静态方法,这些方法在测试类中的任何测试方法运行之前调用,用来建立共享基境。

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @beforeClass
     */
    public static function setUpSomeSharedFixtures()
    {
        // ...
    }

    /**
     * @beforeClass
     */
    public static function setUpSomeOtherSharedFixtures()
    {
        // ...
    }
}

@codeCoverageIgnore*

@codeCoverageIgnore@codeCoverageIgnoreStart@codeCoverageIgnoreEnd 标注用于从覆盖率分析中排出掉某些代码行。

用法参见“忽略代码块”一节

@covers

@covers 标注用在测试代码中,来指明测试方法想要对哪些方法进行测试:

/**
 * @covers BankAccount::getBalance
 */
public function testBalanceIsInitiallyZero()
{
    $this->assertEquals(0, $this->ba->getBalance());
}

如果提供了这个标注,则只考虑指明的这些方法的测试覆盖率信息。

表 B.1列出了 @covers 标注的语法。

表 B.1. 用于指明测试覆盖哪些方法的标注

标注描述
@covers ClassName::methodName指明所标注的测试方法覆盖指定的方法。
@covers ClassName指明所标注的测试方法覆盖给定类的全部方法。
@covers ClassName<extended>指明所标注的测试方法覆盖给定类以及其所有父类与接口的全部方法。
@covers ClassName::<public>指明所标注的测试方法覆盖给定类的所有 public 方法。
@covers ClassName::<protected>指明所标注的测试方法覆盖给定类的所有 protected 方法。
@covers ClassName::<private>指明所标注的测试方法覆盖给定类的所有 private 方法。
@covers ClassName::<!public>指明所标注的测试方法覆盖给定类的所有非 public 方法。
@covers ClassName::<!protected>指明所标注的测试方法覆盖给定类的所有非 protected 方法。
@covers ClassName::<!private>指明所标注的测试方法覆盖给定类的所有非 private 方法。
@covers ::functionName指明所标注的测试方法覆盖给定的全局函数。


@coversDefaultClass

@coversDefaultClass 标注用于指定一个默认的命名空间或类名,这样就不用在每个 @covers 标注中重复长名称。参见例 B.1

例 B.1: 用 @coversDefaultClass 缩短标注

<?php
/**
 * @coversDefaultClass \Foo\CoveredClass
 */
class CoversDefaultClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * @covers ::publicMethod
     */
    public function testSomething()
    {
        $o = new Foo\CoveredClass;
        $o->publicMethod();
    }
}
?>


@coversNothing

@coversNothing 标注用在测试代码中,来指明所标注的测试用例不记录任何代码覆盖率信息。

这可以用于集成测试。例子可参见例 11.3

这个标注可以用在类级别或者方法级别,并且会覆盖掉任何 @covers 标签。

@dataProvider

测试方法可以接受任意参数。这些参数可以由数据供给器方法(例 2.5中的 provider())提供。所要使用的数据供给器方法用 @dataProvider 标注来指定。

更多细节参见“数据供给器”一节

@depends

PHPUnit支持对测试方法之间的显式依赖关系进行声明。这种依赖关系并不是定义在测试方法的执行顺序中,而是允许生产者(producer)返回一个测试基境(fixture)的实例,并将此实例传递给依赖于它的消费者(consumer)们。例 2.2展示了如何用 @depends 标注来表达测试方法之间的依赖关系。

更多细节参见“测试的依赖关系”一节

@expectedException

例 2.9展示了如何用 @expectedException 标注来测试被测代码中是否抛出了异常。

更多细节参见“对异常进行测试”一节

@expectedExceptionCode

@expectedExceptionCode 标注,与 @expectedException 联合使用时,可以对来对抛出的异常的代号作出断言,这样可以缩小具体异常的范围。

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException     MyException
     * @expectedExceptionCode 20
     */
    public function testExceptionHasErrorcode20()
    {
        throw new MyException('Some Message', 20);
    }
}

为了方便测试,并减少冗余,可以在 @expectedExceptionCode 中用"@expectedExceptionCode ClassName::CONST"这样的语法为其指定类常数。

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
      * @expectedException     MyException
      * @expectedExceptionCode MyClass::ERRORCODE
      */
    public function testExceptionHasErrorcode20()
    {
      throw new MyException('Some Message', 20);
    }
}
class MyClass
{
    const ERRORCODE = 20;
}

@expectedExceptionMessage

@expectedExceptionMessage 标注的运作方式类似于 @expectedExceptionCode,它允许你对异常的错误讯息作出断言。

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException        MyException
     * @expectedExceptionMessage Some Message
     */
    public function testExceptionHasRightMessage()
    {
        throw new MyException('Some Message', 20);
    }
}

预期讯息可以是异常讯息的子串。在只需要断言传入的特定名称或参数确实出现于异常中时这个特性很有用,这样就无需在测试中关注完整的异常讯息。

class MyTest extends PHPUnit_Framework_TestCase
{
     /**
      * @expectedException        MyException
      * @expectedExceptionMessage broken
      */
     public function testExceptionHasRightMessage()
     {
         $param = "broken";
         throw new MyException('Invalid parameter "'.$param.'".', 20);
     }
}

为了方便测试,并减少冗余,可以在 @expectedExceptionMessage 中用"@expectedExceptionMessage ClassName::CONST"这样的语法为其指定类常数。 例子可以参看“@expectedExceptionCode”一节

@group

测试可以用 @group 标注来标记为属于一个或多个组,就像这样:

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @group specification
     */
    public function testSomething()
    {
    }

    /**
     * @group regresssion
     * @group bug2204
     */
    public function testSomethingElse()
    {
    }
}

测试可以基于组来选择性的执行,使用命令行测试执行器的 --group--exclude-group 选项,或者使用对应的 XML 配置文件指令。

@large

@large 标注是 @group large 的别名。

如果安装了 PHP_Invoker 组件包并启用了严格模式,一个执行时间超过60秒的大型(large)测试将会视为失败。这个超时限制可以通过 XML 配置文件的 timeoutForLargeTests 属性进行配置。

@medium

@medium 标注是 @group medium 的别名。中型(medium)测试不能依赖于标记为 @large 的测试。

如果安装了 PHP_Invoker 组件包并启用了严格模式,一个执行时间超过10秒的中型(medium)测试将会视为失败。这个超时限制可以通过 XML 配置文件的 timeoutForMediumTests 属性进行配置。

@preserveGlobalState

在单独的进程中运行测试时,PHPUnit 会尝试保持来自父进程的全局状态(通过在父进程序列化全局状态然后在子进程反序列化的方式)。这当父进程包含非可序列化的全局内容时可能会导致问题。为了修正这种问题,可以用 @preserveGlobalState 标注来禁止 PHPUnit 保持全局状态。

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @runInSeparateProcess
     * @preserveGlobalState disabled
     */
    public function testInSeparateProcess()
    {
        // ...
    }
}

@requires

@requires 标注用于在常见前提条件(例如 PHP 版本或所安装的扩展)不满足时跳过测试。

完整的可能用法以及例子见表 7.3

@runTestsInSeparateProcesses

指明单个测试类内的所有测试要各自运行在独立的 PHP 进程中。

/**
 * @runTestsInSeparateProcesses
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    // ...
}

注意: 默认情况下,PHPUnit 会尝试保持来自父进程的全局状态(通过在父进程序列化全局状态然后在子进程反序列化的方式)。这当父进程包含非可序列化的全局内容时可能会导致问题。关于如何修正此问题的信息参见“@preserveGlobalState”一节

@runInSeparateProcess

指明某个测试要运行在独立的 PHP 进程中。

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @runInSeparateProcess
     */
    public function testInSeparateProcess()
    {
        // ...
    }
}

注意: 默认情况下,PHPUnit 会尝试保持来自父进程的全局状态(通过在父进程序列化全局状态然后在子进程反序列化的方式)。这当父进程包含非可序列化的全局内容时可能会导致问题。关于如何修正此问题的信息参见“@preserveGlobalState”一节

@small

@small 标注是 @group small 的别名。小型(small)测试不能依赖于标记为 @medium@large 的测试。

如果安装了 PHP_Invoker 组件包并启用了严格模式,一个执行时间超过1秒的小型(small)测试将会视为失败。这个超时限制可以通过 XML 配置文件的 timeoutForSmallTests 属性进行配置。

注意

默认情况下,所有未标记为 @medium@large 的测试都视为小型(small)测试。请注意,虽然如此,--group 和有关的选项都只会将用恰当的标注显式标记好的测试视为在 small 组中。

@test

除了用 test 作为测试方法名称的前缀外,还可以在方法的文档注释块中用 @test 标注来将其标记为测试方法。

/**
 * @test
 */
public function initialBalanceShouldBe0()
{
    $this->assertEquals(0, $this->ba->getBalance());
}

@testdox

@ticket