PHPUnit 数据提供者
测试方法通常需要测试数据。要完全测试某些方法,你需要为每个可能的测试条件提供不同的数据集。当然,你可以使用循环手动执行此操作,如下所示:
...
public function testSomething()
{
$data = [...];
foreach($data as $dataSet) {
$this->assertSomething($dataSet);
}
}
...
有人可以发现它很方便。但是这种方法存在一些缺点。首先,如果测试函数接受多个参数,则必须执行其他操作来提取数据。其次,在失败时,如果没有附加消息和调试,就很难区分出故障数据集。第三,PHPUnit 提供了使用数据提供程序处理测试数据集的自动方法。
数据提供程序是一个函数,应返回特定测试用例的数据。
数据提供程序方法必须是公共的,并返回一个数组数组或一个实现 Iterator 接口的对象,并为每个迭代步骤生成一个数组。对于作为集合一部分的每个数组,将调用测试方法,并将数组的内容作为其参数。
要在测试中使用数据提供程序,请使用 @dataProvider
annotation 以及指定的数据提供程序函数的名称:
/**
* @dataProvider dataProviderForTest
*/
public function testEquals($a, $b)
{
$this->assertEquals($a, $b);
}
public function dataProviderForTest()
{
return [
[1,1],
[2,2],
[3,2] //this will fail
];
}
数组数组
请注意,
dataProviderForTest()
返回数组数组。每个嵌套数组都有两个元素,它们将逐一填充testEquals()
的必要参数。如果没有足够的元素,这样的错误将被抛出Missing argument 2 for Test::testEquals()
。PHPUnit 将自动循环数据并运行测试:
public function dataProviderForTest()
{
return [
[1,1], // [0] testEquals($a = 1, $b = 1)
[2,2], // [1] testEquals($a = 2, $b = 2)
[3,2] // [2] There was 1 failure: 1) Test::testEquals with data set #2 (3, 4)
];
}
为方便起见,可以命名每个数据集。检测失败的数据会更容易:
public function dataProviderForTest()
{
return [
'Test 1' => [1,1], // [0] testEquals($a = 1, $b = 1)
'Test 2' => [2,2], // [1] testEquals($a = 2, $b = 2)
'Test 3' => [3,2] // [2] There was 1 failure:
// 1) Test::testEquals with data set "Test 3" (3, 4)
];
}
迭代器
class MyIterator implements Iterator {
protected $array = [];
public function __construct($array) {
$this->array = $array;
}
function rewind() {
return reset($this->array);
}
function current() {
return current($this->array);
}
function key() {
return key($this->array);
}
function next() {
return next($this->array);
}
function valid() {
return key($this->array) !== null;
}
}
...
class Test extends TestCase
{
/**
* @dataProvider dataProviderForTest
*/
public function testEquals($a)
{
$toCompare = 0;
$this->assertEquals($a, $toCompare);
}
public function dataProviderForTest()
{
return new MyIterator([
'Test 1' => [0],
'Test 2' => [false],
'Test 3' => [null]
]);
}
}
如你所见,简单的迭代器也可以工作。
请注意,即使对于单个参数,数据提供者也必须返回数组
[$parameter]
因为如果我们将 current()
方法(实际上每次迭代返回数据)更改为:
function current() {
return current($this->array)[0];
}
或者更改实际数据:
return new MyIterator([
'Test 1' => 0,
'Test 2' => false,
'Test 3' => null
]);
我们会收到一个错误:
There was 1 warning:
1) Warning
The data provider specified for Test::testEquals is invalid.
当然,在简单数组上使用
Iterator
对象是没有用的。它应该为你的案例实现一些特定的逻辑。
发电机
它没有明确说明并在手册中显示,但你也可以使用生成器作为数据提供者。请注意,Generator
类实际上实现了 Iterator
接口。
这里有一个使用 DirectoryIterator
与 generator
结合的例子:
/**
* @param string $file
*
* @dataProvider fileDataProvider
*/
public function testSomethingWithFiles($fileName)
{
//$fileName is available here
//do test here
}
public function fileDataProvider()
{
$directory = new DirectoryIterator('path-to-the-directory');
foreach ($directory as $file) {
if ($file->isFile() && $file->isReadable()) {
yield [$file->getPathname()]; // invoke generator here.
}
}
}
注意提供者
yield
s 是一个数组。你将收到无效的数据提供者警告。