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 是一個陣列。你將收到無效的資料提供者警告。