本文目录导读:

在 PHP 中进行数据驱动测试有多种方式,下面我来介绍几种主流的方法:
使用 PHPUnit 的 Data Provider
这是最常用的方式,通过 @dataProvider 注解提供测试数据:
<?php
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
/**
* @dataProvider additionProvider
*/
public function testAdd($a, $b, $expected)
{
$calculator = new Calculator();
$this->assertEquals($expected, $calculator->add($a, $b));
}
public function additionProvider()
{
return [
'case1: positive numbers' => [1, 2, 3],
'case2: negative numbers' => [-1, -2, -3],
'case3: zeros' => [0, 0, 0],
'case4: mixed' => [5, -3, 2],
];
}
}
从外部文件读取测试数据
CSV 文件
<?php
class UserServiceTest extends TestCase
{
/**
* @dataProvider csvUserProvider
*/
public function testValidateUser($name, $age, $expected)
{
$service = new UserService();
$this->assertEquals($expected, $service->validate($name, $age));
}
public function csvUserProvider()
{
$data = [];
$file = fopen(__DIR__ . '/test_data/users.csv', 'r');
while (($row = fgetcsv($file)) !== false) {
$data[] = $row;
}
fclose($file);
return $data;
}
}
JSON 文件
<?php
class ProductTest extends TestCase
{
/**
* @dataProvider jsonProductProvider
*/
public function testProductPrice($productData, $expectedPrice)
{
$product = new Product($productData);
$this->assertEquals($expectedPrice, $product->getPrice());
}
public function jsonProductProvider()
{
$json = file_get_contents(__DIR__ . '/test_data/products.json');
$data = json_decode($json, true);
$result = [];
foreach ($data as $case) {
$result[$case['name']] = [$case['data'], $case['expected']];
}
return $result;
}
}
使用数据集合(数组)驱动
<?php
class DatabaseTest extends TestCase
{
private $connection;
public function setUp(): void
{
// 数据库连接初始化
}
/**
* @dataProvider databaseTestCases
*/
public function testDatabaseOperations($sql, $params, $expectedResult)
{
$result = $this->connection->execute($sql, $params);
$this->assertEquals($expectedResult, $result);
}
public function databaseTestCases()
{
// 测试数据可以从配置文件、数据库或硬编码获取
return [
'select user by id' => [
'SELECT * FROM users WHERE id = ?',
[1],
['id' => 1, 'name' => 'John']
],
'select user by email' => [
'SELECT * FROM users WHERE email = ?',
['john@example.com'],
['id' => 1, 'email' => 'john@example.com']
],
];
}
}
使用命名空间或其他工具库
使用数组组合生成(多参数交叉测试)
<?php
class ValidationTest extends TestCase
{
/**
* @dataProvider validationDataProvider
*/
public function testEmailValidation($email, $expected)
{
$validator = new EmailValidator();
$this->assertEquals($expected, $validator->validate($email));
}
public function validationDataProvider()
{
// 定义多组测试数据
$validEmails = ['test@example.com', 'user.name@domain.com'];
$invalidEmails = ['invalid-email', 'test@', '@test.com'];
$data = [];
foreach ($validEmails as $email) {
$data["Valid email: $email"] = [$email, true];
}
foreach ($invalidEmails as $email) {
$data["Invalid email: $email"] = [$email, false];
}
return $data;
}
}
高级用法:生成器提供数据
<?php
class ApiTest extends TestCase
{
/**
* @dataProvider apiDataProvider
*/
public function testApiEndpoint($endpoint, $payload, $expectedStatus)
{
$client = new GuzzleHttp\Client();
$response = $client->request('POST', $endpoint, ['json' => $payload]);
$this->assertEquals($expectedStatus, $response->getStatusCode());
}
public function apiDataProvider()
{
// 使用生成器懒加载数据
return (function () {
$testCases = [
['/api/login', ['username' => 'admin', 'password' => '123456'], 200],
['/api/login', ['username' => 'test', 'password' => 'wrong'], 401],
['/api/register', ['username' => 'newuser', 'password' => 'test123'], 201],
];
foreach ($testCases as $case) {
yield $case;
}
})();
}
}
测试数据组织最佳实践
<?php
class DataDrivenTestTest extends TestCase
{
/**
* 测试配置:使用 YAML 或数组配置
*/
public function testConfigDriven()
{
$config = [
'test1' => ['input' => 'value1', 'expected' => 'result1'],
'test2' => ['input' => 'value2', 'expected' => 'result2'],
// ...
];
foreach ($config as $caseName => $testData) {
$result = $this->process($testData['input']);
$this->assertEquals($testData['expected'], $result, "Case: $caseName failed");
}
}
private function process($input)
{
// 业务逻辑
return $input . '_processed';
}
}
最佳实践建议:
- 数据与测试逻辑分离:将测试数据放在外部文件中(JSON、CSV、YAML)
- 使用有意义的名称:为每个测试用例提供描述性名称
- 保持数据独立性:每个测试用例应该相互独立
- 注意性能:数据量大时考虑使用生成器
- 错误信息清晰:添加有意义的错误信息
<?php
// 最佳实践示例
class DataDrivenExampleTest extends TestCase
{
/**
* @dataProvider excellentDataProvider
*/
public function testExample($caseName, $input, $expected)
{
$result = $this->processData($input);
$this->assertEquals($expected, $result, "Case '$caseName' failed");
}
public function excellentDataProvider()
{
// 从 JSON 文件读取并附带描述
$dataFile = __DIR__ . '/fixtures/test_cases.json';
$cases = json_decode(file_get_contents($dataFile), true);
$provider = [];
foreach ($cases as $case) {
$provider[$case['description']] = [
$case['description'],
$case['input'],
$case['expected']
];
}
return $provider;
}
}
数据驱动测试的核心思想是:将测试数据从测试逻辑中分离出来,这样可以更容易地增加、修改测试用例,而不需要修改测试代码本身。