PHPUnit学习笔记
PHPUnit是一个轻量级的PHP测试框架。它是在PHP5下面对JUnit3系列版本的完整移植,是xUnit测试框架家族的一员(它们都基于模式先锋Kent Beck的设计)
PHPUnit 编写测试的基本惯例与步骤
-
针对类 Class 的测试写在类 ClassTest中。
-
ClassTest(通常)继承自 PHPUnit_Framework_TestCase。
-
测试都是命名为 test* 的公用方法。
-
也可以在方法的文档注释块(docblock)中使用 @test 标注将其标记为测试方法。
-
在测试方法内,类似于 assertEquals()这样的断言方法用来对实际值与预期值的匹配做出断言。
例:用 PHPUnit 测试数组操作
` assertEquals(0, count($stack)); array_push($stack, 'foo'); $this->assertEquals('foo', $stack[count($stack)-1]); $this->assertEquals(1, count($stack)); $this->assertEquals('foo', array_pop($stack)); $this->assertEquals(0, count($stack)); } /** * 定义test标签声明改方法是测试方法 * @test */ public function indexEquals() { $stack = array(1,2,3,4); $this->assertEquals(2,$stack[0]); //失败 } } ?> `
- 通俗的讲,单元测试就是在测试用例类中,定义一系列的测试方法,在方法中使用断言(assert)来测试你程序中的相关函数、类、接口、过程的执行结果是否和你预期的是一样的,
- 如果某个部分的执行结果没有与你期待的结果相同,PHPUnit就会向你报告问题,你也可以方便的根据报告确认和修复程序中的Bug。针对你的程序写好详细的完整单元测试,你就不用每次完成一个新功能后逐个的测试你软件的所有功能,而且在程序发布之前,你至少可以通过测试消灭大部分的内部逻辑Bug和缺陷。
- 同样,你在之后的维护开发中,如果在某个功能的修改中不小心改坏了与其相关连的其他模块,那么先前完善的单元测试也会向你报告出相关的问题让你及时发现和修复问题。
测试方法间的依赖关系
- 测试方法并不一定非要是一个封装良好的独立实体。测试方法之间经常有隐含的依赖关系暗藏在测试的实现方案中。
- 依赖关系通过注释@depends来定义.这个特性一般用于检查代码的逻辑过程,一个逻辑的执行前提是另外一个逻辑的执行结果
例:用 @depends 标注来表达依赖关系
<?php
class StackTest extends PHPUnit_Framework_TestCase
{
public function testEmpty()
{
$stack = array();
$this->assertEmpty($stack);
return $stack;
}
/**
* @depends testEmpty
*/
public function testPush(array $stack)
{
array_push($stack, 'foo');
$this->assertEquals('foo', $stack[count($stack)-1]);
$this->assertNotEmpty($stack);
return $stack;
}
/**
* @depends testPush
*/
public function testPop(array $stack)
{
$this->assertEquals('foo', array_pop($stack));
$this->assertEmpty($stack);
}
}
?>
复制代码
- 只要断言检查没有出现异常,那么PHPUnit就会根据依赖关系依次执行依赖的测试方法,直到依赖关系结束为止.
- 为了方便快速的确定问题的所在,如果某个测试方法依赖的方法测试没有功过,那么PHPUnit会自动跳过后面所有的依赖测试.
*** 拥有多个 @depends 标注的测试,其第一个参数是第一个生产者提供的基境,第二个参数是第二个生产者提供的基境,以此类推 ***
` assertTrue(true); return 'first'; } public function testProducerSecond() { $this->assertTrue(true); return 'second'; } /** 多重依赖 * @depends testProducerFirst * @depends testProducerSecond */ public function testConsumer() { $this->assertEquals( array('first', 'second'), func_get_args() ); } } ?>`
数据供给器
- 某个测试方法没有依赖的方法,我们怎么给它传入参数做测试呢?PHPUnit给我们提供了数据提供者方法来为测试方法传入数据.测试方法可以接受任意参数。这些参数由数据供给器方法提供。用 @dataProvider 标注来指定使用哪个数据供给器方法。
- 数据提供者方法返回数据的格式: 需要返回的是2维数组,第二维数组值的位置,对应测试方法参数的位置,参数个数和数组长度要相等,否则PHPUnit会报错
例:使用返回数组的数组的数据供给器
` assertEquals($expected, $a + $b); } public function additionProvider() { return array( array(0, 0, 0), array(0, 1, 1), array(1, 0, 1), array(1, 1, 3) ); } } ?> `
- 如果测试同时从 @dataProvider 方法和一个或多个 @depends 测试接收数据,那么来自于数据供给器的参数将先于来自所依赖的测试的。来自于所依赖的测试的参数对于每个数据集都是一样的。
例:
` assertTrue(true); return 'first'; } public function testProducerSecond() { $this->assertTrue(true); return 'second'; } /** * @depends testProducerFirst * @depends testProducerSecond * @dataProvider provider */ public function testConsumer() { $this->assertEquals( array('provider1', 'first', 'second'), func_get_args() ); } } ?> `
** 结果:Tests: 4, Assertions: 4, Failures: 1. **
@depends @dataProvider @dataProvider 是一个二维数组, 有多少个第二纬的数组就测试多少次,第二纬的数组和参数一致 @depends 生产者(producer)返回一个测试基境(fixture)的实例,并将此实例传递给依赖于它的消费者(consumer)们。二者同时存在的话先进行@dataProvider
对异常进行测试
例:
` expectException(InvalidArgumentException::class); } } ?> `
- 使用 @expectedException 标注
` `
对输出进行测试
错误相关信息的输出
边缘情况
基境(fixture)
PHPUnit 支持共享建立基境的代码。在运行某个测试方法前,会调用一个名叫 setUp() 的模板方法。setUp() 是创建测试所用对象的地方。当测试方法运行结束后,不管是成功还是失败,都会调用另外一个名叫 tearDown() 的模板方法。tearDown() 是清理测试所用对象的地方。
在编写测试用例的时候可能最费时间的就是编写那些将程序设置到使用状态和测试完毕之后将其再设置回初始状态的代码了。PHPUnit给我们提供了setUp和tearDown这两个方法来解决这个问题。
测试类的每个测试方法都会运行一次 setUp() 和 tearDown() 模板方法(同时,每个测试方法都是在一个全新的测试类实例上运行的)。
setUpBeforeClass() 与 tearDownAfterClass() 模板方法将分别在测试用例类的第一个测试运行之前和测试用例类的最后一个测试运行之后调用。
组织测试
- 用文件系统来编排测试套件
- 用 XML 配置来编排测试套件