支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
PHPUnit Bridge提供的功能包括,报告遗产级测试(legacy tests)和不建议使用的代码之用法,以及一个“对时间敏感的”测试的助手(a helper for time-sensitive tests)。
它包括以下功能:
C);class_exists 以加载Doctrine annotations (当使用时);ClockMock helper类。你可以通过下述两种方式安装:
通过Composer安装(Packagist上的symfony/phpunit-bridge)
通过官方Git宝库(https://github.com/symfony/phpunit-bridge)
然后,包容vendor/autoload.php文件,以开启Composer提供的自动加载机制。否则,你的程序将无法找到这个Symfony组件的类。
组件一旦安装完毕,它就自动注册一个 PHPUnit event listener,用于后续注册一个名为 DeprecationErrorHandler 的 PHP error handler 。在你运行完PHPUnit测试后,你会看到类似下图这样一个“报告”:

报告总结了以下内容:
“不建议使用”的通知,可以通过使用以下例程中的代码来触发:
1 | @trigger_error('Your deprecation message', E_USER_DEPRECATED); |
若不加 @-silencing 操作符,用户就得从“不建议使用”的通知信息中选择退出。默认“静默”的话可以避免触发通知,并让用户在他们准备好去解决这些问题时(通过添加自定义的error handler,像是由这个bridge所提供的那个)选择进入。当不静默时,“不建议使用”的通知将显示在“Deprecation报告”的 Unsilenced 区块中。
有四种方式可以把一个测试标记为遗产(legacy):
@group legacy annotation到测试类和测试方法中;Legacy 前缀;testLegacy 开头而不是(常规的) test;provideLegacy 或 getLegacy 开头。如果你需要检查一个由“单元测试”所触发的“特定deprecation通知”的stack trace(栈追踪),你可以把 SYMFONY_DEPRECATIONS_HELPER environment variable(环境变量)设置成一个“能够匹配这条deprecation信息”的正则表达式,它由 / 封闭。例程:
1 2 3 4 5 6 7 8 9 10 11 12 | <!-- http://phpunit.de/manual/4.1/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
>
<!-- ... -->
<php>
<server name="KERNEL_DIR" value="app/" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="/foobar/" />
</php>
</phpunit> |
PHPUnit 将在含有 "foobar" 字符串的deprecation通知被触发时,中止你的测试包。
默认时,任何 non-legacy-tagged(未标记为遗产) 或 non-@-silenced (非静默) 的deprecation通知,都将令测试失败。一种方案是,设置 SYMFONY_DEPRECATIONS_HELPER 为任意值 (如: 320) 可以令测试仅在一个更大数字的deprecation通知到来时 (0 是默认值) 才失败。你也可以把这个值设为 "weak",这将令bridge忽略任何deprecation信息。这对那些因向下兼容的原因而必须使用“不建议使用”之接口的项目来说十分有用。
3.1 关闭Deprecation Helper的能力,从Symfony 3.1开始被引入。
设置 SYMFONY_DEPRECATIONS_HELPER 环境变量为 disabled 以彻底禁用 deprecation helper。这在使用本组件提供的其他功能时有用,不会再收到错误信息和deprecation通知。
如果你有和时间相关的测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | use Symfony\Component\Stopwatch\Stopwatch;
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$stopwatch = new Stopwatch();
$stopwatch->start();
sleep(10);
$duration = $stopwatch->stop();
$this->assertEquals(10, $duration);
}
} |
你使用了 Symfony Stopwatch 组件 来计算进程持续时间,这里是10秒。但是,根据“运行在你本地机器上的进程”对Server带来的负载, $duration 值可能会是 10.000023s 而非 10s。
这类测试被统称为 transient tests(瞬态测试): 它们会受到伪造或外部环境的影响而失败。当使用公开的、持续的整合型服务比如 Travis CI 时,它们经常引起麻烦。
这个bridge提供的 ClockMock 类,允许你mock PHP内置的时间函数 time(), microtime(), sleep() 和 usleep()。
要在测试中使用 ClockMock 类,你可以:
@group time-sensitive annotation 测试类或测试方法;ClockMock::register(__CLASS__) 和 ClockMock::withClockMock(true) ,可在测试之前手动注册它,或是在测试之后,调用 ClockMock::withClockMock(false) 来注册。作为结果,下列代码确保正常运行,并且不是一个transient test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | use Symfony\Component\Stopwatch\Stopwatch;
/**
* @group time-sensitive
*/
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$stopwatch = new Stopwatch();
$stopwatch->start();
sleep(10);
$duration = $stopwatch->stop();
$this->assertEquals(10, $duration);
}
} |
就是这些了!
使用 ClockMock 类的一个附带的好处是,时间迅即流过。使用PHP的 sleep(10) 会令你的测试真正等待10秒(或多或少)。对比来看,ClockMock 类将内置的时钟给提前到一个给定的秒数,毋须真正经过这个时间,因此你的测试在执行时要快过10秒。
3.1 DNS相关函数的mocks,从Symfony 3.1开始被引入。
包含网络连接的测试,比如要检查DNS纪录的有效性,会因为网络条件而执行的很慢,或是不稳定。因此,本组件也提供了对这一类PHP函数的模拟(mock):
checkdnsrrdns_check_recordgetmxrrdns_get_mxgethostbyaddrgethostbynamegethostbynameldns_get_record思考以下“使用了 Email 约束中的 checkMX 选项,来测试email域名有效性”的例程:
1 2 3 4 5 6 7 8 9 10 11 12 13 | use Symfony\Component\Validator\Constraints\Email;
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testEmail()
{
$validator = ...
$constraint = new Email(array('checkMX' => true));
$result = $validator->validate('foo@example.com', $constraint);
// ...
} |
为了避免产生真实的网络连接,添加 @dns-sensitive annotation 到测试类,并使用 DnsMock::withMockedHosts() 来配置 “你预期得到并用于给定义机” 的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | use Symfony\Component\Validator\Constraints\Email;
/**
* @group dns-sensitive
*/
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testEmails()
{
DnsMock::withMockedHosts(array('example.com' => array(array('type' => 'MX'))));
$validator = ...
$constraint = new Email(array('checkMX' => true));
$result = $validator->validate('foo@example.com', $constraint);
// ...
} |
withMockedHosts() 配置方法,被定义为一个数组。它的键是模拟的主机,值是DNS记录的数组,其格式与 dns_get_record 方法返回的相同,因此你可以模拟各种不同的网络条件:
@group time-sensitive 和 @group dns-sensitive annotations 都是 "根据约定" 来工作,它们假定“被测试类”的命名空间可以通过从测试类中移除 Tests\ 部分来获得。也就是,如果你的test case的 fully-qualified class name (FQCN类名) 是 App\Tests\Watch\DummyWatchTest,它会推定被测试类的命名空间是 App\Watch。
如果这种约定在你的程序中无法运行,在 phpunit.xml 文件中配置被模拟的命名空间,如同在 HttpKernel组件 中所展示的那样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <!-- http://phpunit.de/manual/4.1/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
>
<!-- ... -->
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener">
<arguments>
<array>
<element><string>Symfony\Component\HttpFoundation</string></element>
</array>
</arguments>
</listener>
</listeners>
</phpunit> |
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。