4. 基境(fixture)
最后更新于:2022-04-01 03:44:44
# 第 4 章 基境(fixture)
在编写测试时,最费时的部分之一是编写代码来将整个场景设置成某个已知的状态,并在测试结束后将其复原到初始状态。这个已知的状态称为测试的 *基境(fixture)*。
在[Example 2.1, “用 PHPUnit 测试数组操作”](# "Example 2.1. 用 PHPUnit 测试数组操作")中,基境十分简单,就是存储在 `$stack` 变量中的数组。然而,绝大多数时候基境均远比一个简单数组要复杂,用于建立基境的代码量也会随之增长。测试的真正内容就被淹没于建立基境带来的干扰中。当编写多个需要类似基境的测试时这个问题就变得更糟糕了。如果没有来自于测试框架的帮助,就不得不在写每一个测试时都将建立基境的代码重复一次。
PHPUnit 支持共享建立基境的代码。在运行某个测试方法前,会调用一个名叫 `setUp()` 的模板方法。`setUp()` 是创建测试所用对象的地方。当测试方法运行结束后,不管是成功还是失败,都会调用另外一个名叫 `tearDown()` 的模板方法。`tearDown()` 是清理测试所用对象的地方。
在[Example 2.2, “用 `@depends` 标注来表达依赖关系”](# "Example 2.2. 用 @depends 标注来表达依赖关系")中,我们在测试之间运用生产者-消费者关系来共享基境。这并非总是预期的方式,甚至有时是不可能的。[Example 4.1, “用 setUp() 建立栈的基境”](# "Example 4.1. 用 setUp() 建立栈的基境")展示了另外一个编写测试 `StackTest` 的方式。在这个方式中,不再重用基境本身,而是重用建立基境的代码。首先声明一个实例变量,`$stack`,用来替代方法内的局部变量。然后把 `array` 基境的建立放到 `setUp()` 方法中。最后,从测试方法中去除冗余代码,在 `assertEquals()` 断言方法中使用新引入的实例变量 `$this->stack`替代方法内的局部变量 `$stack`。
**Example 4.1. 用 setUp() 建立栈的基境**
~~~
<?php
class StackTest extends PHPUnit_Framework_TestCase
{
protected $stack;
protected function setUp()
{
$this->stack = array();
}
public function testEmpty()
{
$this->assertTrue(empty($this->stack));
}
public function testPush()
{
array_push($this->stack, 'foo');
$this->assertEquals('foo', $this->stack[count($this->stack)-1]);
$this->assertFalse(empty($this->stack));
}
public function testPop()
{
array_push($this->stack, 'foo');
$this->assertEquals('foo', array_pop($this->stack));
$this->assertTrue(empty($this->stack));
}
}
?>
~~~
测试类的每个测试方法都会运行一次 `setUp()` 和 `tearDown()` 模板方法(同时,每个测试方法都是在一个全新的测试类实例上运行的)。
另外,`setUpBeforeClass()` 与 `tearDownAfterClass()` 模板方法将分别在测试用例类的第一个测试运行之前和测试用例类的最后一个测试运行之后调用。
下面这个例子中展示了测试用例类中所有可用的模板方法。
**Example 4.2. 展示所有可用模板方法的例子**
~~~
<?php
class TemplateMethodsTest extends PHPUnit_Framework_TestCase
{
public static function setUpBeforeClass()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
protected function setUp()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
protected function assertPreConditions()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
public function testOne()
{
fwrite(STDOUT, __METHOD__ . "\n");
$this->assertTrue(TRUE);
}
public function testTwo()
{
fwrite(STDOUT, __METHOD__ . "\n");
$this->assertTrue(FALSE);
}
protected function assertPostConditions()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
protected function tearDown()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
public static function tearDownAfterClass()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
protected function onNotSuccessfulTest(Exception $e)
{
fwrite(STDOUT, __METHOD__ . "\n");
throw $e;
}
}
?>
~~~
~~~
phpunit TemplateMethodsTest
PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
TemplateMethodsTest::setUpBeforeClass
TemplateMethodsTest::setUp
TemplateMethodsTest::assertPreConditions
TemplateMethodsTest::testOne
TemplateMethodsTest::assertPostConditions
TemplateMethodsTest::tearDown
.TemplateMethodsTest::setUp
TemplateMethodsTest::assertPreConditions
TemplateMethodsTest::testTwo
TemplateMethodsTest::tearDown
TemplateMethodsTest::onNotSuccessfulTest
FTemplateMethodsTest::tearDownAfterClass
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) TemplateMethodsTest::testTwo
Failed asserting that <boolean:false> is true.
/home/sb/TemplateMethodsTest.php:30
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
~~~