本文目录导读:

我来详细介绍ThinkPHP项目中如何进行模拟请求和断言测试。
使用PHPUnit进行接口测试
1 安装依赖
composer require phpunit/phpunit --dev composer require guzzlehttp/guzzle --dev
2 创建测试基类
<?php
// tests/TestCase.php
namespace tests;
use PHPUnit\Framework\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
protected $app;
protected $httpClient;
protected function setUp(): void
{
parent::setUp();
// 初始化ThinkPHP应用
$this->app = new \think\App();
$this->app->initialize();
// 创建Guzzle客户端
$this->httpClient = new \GuzzleHttp\Client([
'base_uri' => 'http://localhost:8080',
'timeout' => 5.0,
'http_errors' => false
]);
}
/**
* 发送GET请求
*/
protected function get($uri, $headers = [])
{
try {
$response = $this->httpClient->get($uri, [
'headers' => array_merge([
'Accept' => 'application/json',
], $headers)
]);
return $this->parseResponse($response);
} catch (\Exception $e) {
return [
'status' => 500,
'body' => ['message' => $e->getMessage()],
'raw' => $e
];
}
}
/**
* 发送POST请求
*/
protected function post($uri, $data = [], $headers = [])
{
try {
$response = $this->httpClient->post($uri, [
'json' => $data,
'headers' => array_merge([
'Accept' => 'application/json',
], $headers)
]);
return $this->parseResponse($response);
} catch (\Exception $e) {
return [
'status' => 500,
'body' => ['message' => $e->getMessage()],
'raw' => $e
];
}
}
/**
* 解析响应
*/
protected function parseResponse($response)
{
$statusCode = $response->getStatusCode();
$content = $response->getBody()->getContents();
$json = json_decode($content, true);
return [
'status' => $statusCode,
'body' => $json !== null ? $json : ['raw' => $content],
'headers' => $response->getHeaders()
];
}
/**
* 断言响应状态码
*/
protected function assertStatus($response, $expectedStatus)
{
$this->assertEquals($expectedStatus, $response['status'],
"Expected status code {$expectedStatus}, got {$response['status']}");
}
/**
* 断言响应包含指定字段
*/
protected function assertResponseHasKey($response, $key)
{
$this->assertArrayHasKey($key, $response['body'],
"Response does not contain key '{$key}'");
}
/**
* 断言响应等于预期值
*/
protected function assertResponseEquals($response, $expected, $key = null)
{
if ($key !== null) {
$this->assertEquals($expected, $response['body'][$key] ?? null);
} else {
$this->assertEquals($expected, $response['body']);
}
}
}
创建具体的测试类
1 用户接口测试
<?php
// tests/Api/UserTest.php
namespace tests\Api;
use tests\TestCase;
class UserTest extends TestCase
{
protected $token;
/**
* 测试用户登录
* @test
*/
public function testLogin()
{
$response = $this->post('/api/user/login', [
'username' => 'admin',
'password' => '123456'
]);
$this->assertStatus($response, 200);
$this->assertResponseHasKey($response, 'token');
$this->assertResponseHasKey($response, 'user_info');
$this->token = $response['body']['token'];
return $response['body']['token'];
}
/**
* 测试获取用户信息
* @test
* @depends testLogin
*/
public function testGetUserInfo($token)
{
$response = $this->get('/api/user/info', [
'Authorization' => "Bearer {$token}"
]);
$this->assertStatus($response, 200);
$this->assertResponseEquals($response, 'success', 'message');
$this->assertResponseHasKey($response, 'data');
}
/**
* 测试更新用户信息
* @test
*/
public function testUpdateUser()
{
$token = $this->testLogin()['token'] ?? '';
$response = $this->post('/api/user/update', [
'nickname' => '测试用户',
'email' => 'test@example.com'
], [
'Authorization' => "Bearer {$token}"
]);
$this->assertStatus($response, 200);
$this->assertResponseEquals($response, 1, 'code');
}
/**
* 测试用户列表(带token)
* @test
*/
public function testGetUserList()
{
$token = $this->testLogin()['token'] ?? '';
$response = $this->get('/api/user/list?page=1&limit=10', [
'Authorization' => "Bearer {$token}"
]);
$this->assertStatus($response, 200);
$this->assertResponseHasKey($response, 'list');
$this->assertResponseHasKey($response, 'total');
}
}
2 订单接口测试
<?php
// tests/Api/OrderTest.php
namespace tests\Api;
use tests\TestCase;
class OrderTest extends TestCase
{
protected $token;
public function setUp(): void
{
parent::setUp();
// 获取token
$loginResponse = $this->post('/api/user/login', [
'username' => 'admin',
'password' => '123456'
]);
$this->token = $loginResponse['body']['token'] ?? '';
}
/**
* 创建订单
* @test
*/
public function testCreateOrder()
{
$orderData = [
'order_no' => 'ORD' . time(),
'total_amount' => 99.50,
'items' => [
['product_id' => 1, 'quantity' => 2, 'price' => 49.75]
]
];
$response = $this->post('/api/order/create', $orderData, [
'Authorization' => "Bearer {$this->token}"
]);
$this->assertStatus($response, 200);
$this->assertResponseHasKey($response, 'order_id');
}
/**
* 查询订单详情
* @test
*/
public function testGetOrderInfo()
{
$orderId = 1;
$response = $this->get("/api/order/info/{$orderId}", [
'Authorization' => "Bearer {$this->token}"
]);
$this->assertStatus($response, 200);
$this->assertResponseHasKey($response, 'data');
$this->assertArrayHasKey('order_no', $response['body']['data'] ?? []);
}
/**
* 批量断言测试
* @test
*/
public function testApiResponseStructure()
{
$response = $this->get('/api/user/list', [
'Authorization' => "Bearer {$this->token}"
]);
// 多字段断言
$expectedKeys = ['code', 'message', 'data'];
foreach ($expectedKeys as $key) {
$this->assertArrayHasKey($key, $response['body'], "Missing key: {$key}");
}
// 类型断言
$this->assertIsInt($response['body']['code'] ?? null);
$this->assertIsString($response['body']['message'] ?? null);
$this->assertIsArray($response['body']['data'] ?? null);
// 范围断言
$this->assertGreaterThan(-1, $response['body']['code'] ?? -1);
$this->assertLessThan(1000, $response['body']['code'] ?? 999);
}
}
使用ThinkPHP内置测试组件
1 创建测试基类(使用ThinkPHP的测试支持)
<?php
// tests/BaseTest.php
namespace tests;
use think\App;
use think\Container;
use PHPUnit\Framework\TestCase;
class BaseTest extends TestCase
{
/** @var App */
protected $app;
/** @var Container */
protected $container;
/** @var \think\Request */
protected $request;
protected function setUp(): void
{
parent::setUp();
$this->app = new App();
$this->container = Container::getInstance();
$this->request = $this->app->request;
}
/**
* 模拟请求
*/
protected function request($method, $url, $data = [], $headers = [])
{
// 构造请求对象
$this->request->setMethod($method);
$this->request->url($url);
// 设置请求数据
$this->request->withInput($data);
// 设置请求头
foreach ($headers as $key => $value) {
$this->request->header([$key => $value]);
}
// 执行路由
$response = $this->app->http->run($this->request);
return $response;
}
/**
* 模拟GET请求
*/
protected function get($url, $params = [], $headers = [])
{
$url = $url . (strpos($url, '?') ? '&' : '?') . http_build_query($params);
return $this->request('GET', $url, [], $headers);
}
/**
* 模拟POST请求
*/
protected function post($url, $data = [], $headers = [])
{
return $this->request('POST', $url, $data, $headers);
}
/**
* 模拟PUT请求
*/
protected function put($url, $data = [], $headers = [])
{
return $this->request('PUT', $url, $data, $headers);
}
/**
* 模拟DELETE请求
*/
protected function delete($url, $headers = [])
{
return $this->request('DELETE', $url, [], $headers);
}
/**
* 从响应中提取数据
*/
protected function responseData($response)
{
$content = $response->getContent();
return json_decode($content, true);
}
}
2 使用ThinkPHP控制器测试
<?php
// tests/Controller/UserControllerTest.php
namespace tests\Controller;
use tests\BaseTest;
use think\facade\Route;
class UserControllerTest extends BaseTest
{
/**
* 测试用户登录控制器
*/
public function testLoginAction()
{
// 定义路由
Route::post('/api/login', 'UserController@login');
$data = [
'username' => 'admin',
'password' => '123456'
];
$response = $this->post('/api/login', $data);
$result = $this->responseData($response);
$this->assertEquals(200, $response->getCode());
$this->assertTrue($result['success']);
$this->assertArrayHasKey('token', $result['data']);
}
/**
* 测试用户列表控制器
*/
public function testListAction()
{
$response = $this->get('/api/users', ['page' => 1]);
$result = $this->responseData($response);
$this->assertEquals(200, $response->getCode());
$this->assertArrayHasKey('users', $result['data']);
$this->assertIsArray($result['data']['users']);
}
}
高级断言技巧
1 数据校验断言
<?php
// tests/Traits/AssertionHelpers.php
namespace tests\Traits;
trait AssertionHelpers
{
/**
* 断言数据为空或null
*/
public function assertEmptyOrNull($value, $message = '')
{
$this->assertTrue(empty($value), $message);
}
/**
* 断言数据格式正确(邮箱、手机号等)
*/
public function assertValidEmail($email, $message = '')
{
$this->assertMatchesRegularExpression(
'/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/',
$email,
$message
);
}
/**
* 断言时间格式
*/
public function assertValidDateTime($datetime, $message = '')
{
$this->assertMatchesRegularExpression(
'/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/',
$datetime,
$message
);
}
/**
* 断言数组包含指定键
*/
public function assertArrayContainsKeys($array, $keys, $message = '')
{
foreach ($keys as $key) {
$this->assertArrayHasKey($key, $array,
$message ?: "Array should contain key '{$key}'");
}
}
/**
* 断言响应成功状态
*/
public function assertResponseSuccess($response, $message = '')
{
$this->assertEquals(200, $response['status'] ?? null, $message);
$this->assertEquals(1, $response['body']['code'] ?? null, $message);
$this->assertEquals('success', $response['body']['message'] ?? null, $message);
}
}
2 断言链式测试
<?php
// tests/ChainedTest.php
namespace tests;
use tests\Traits\AssertionHelpers;
class ChainedTest extends TestCase
{
use AssertionHelpers;
/**
* 链式测试示例
* @test
*/
public function testChainedAssertions()
{
// 模拟响应数据
$response = [
'status' => 200,
'body' => [
'code' => 1,
'message' => 'success',
'data' => [
'user' => [
'id' => 1,
'username' => 'admin',
'email' => 'admin@example.com',
'created_at' => '2023-01-01 10:00:00'
],
'orders' => [
['id' => 1, 'total' => 100],
['id' => 2, 'total' => 200]
]
]
]
];
// 链式断言
$this->assertResponseSuccess($response)
->assertArrayContainsKeys($response['body']['data'], ['user', 'orders'])
->assertValidEmail($response['body']['data']['user']['email'])
->assertValidDateTime($response['body']['data']['user']['created_at'])
->assertCount(2, $response['body']['data']['orders'])
->assertGreaterThan(0, $response['body']['data']['orders'][0]['total']);
}
}
使用测试辅助工具
1 创建测试工具类
<?php
// tests/Helpers/TestHelper.php
namespace tests\Helpers;
class TestHelper
{
/**
* 生成测试数据
*/
public static function generateTestData($type = 'user')
{
switch ($type) {
case 'user':
return [
'username' => 'test_' . time(),
'password' => '123456',
'email' => 'test_' . time() . '@example.com'
];
case 'order':
return [
'order_no' => 'ORD' . time(),
'amount' => rand(100, 9999) / 100
];
default:
return [];
}
}
/**
* 基础认证头
*/
public static function authHeader($token)
{
return [
'Authorization' => "Bearer {$token}",
'Accept' => 'application/json',
'Content-Type' => 'application/json'
];
}
}
2 使用测试工具类
<?php
// tests/Api/IntegrationTest.php
namespace tests\Api;
use tests\TestCase;
use tests\Helpers\TestHelper;
class IntegrationTest extends TestCase
{
/**
* 完整的业务流测试
* @test
*/
public function testCompleteBusinessFlow()
{
// 1. 用户注册
$userData = TestHelper::generateTestData('user');
$registerResponse = $this->post('/api/register', $userData);
$this->assertStatus($registerResponse, 200);
// 2. 用户登录
$loginResponse = $this->post('/api/login', [
'username' => $userData['username'],
'password' => $userData['password']
]);
$this->assertStatus($loginResponse, 200);
$token = $loginResponse['body']['token'];
// 3. 创建订单
$orderData = TestHelper::generateTestData('order');
$orderResponse = $this->post('/api/orders', $orderData,
TestHelper::authHeader($token));
$this->assertStatus($orderResponse, 200);
$orderId = $orderResponse['body']['order_id'];
// 4. 查询订单
$getOrderResponse = $this->get("/api/orders/{$orderId}",
TestHelper::authHeader($token));
$this->assertStatus($getOrderResponse, 200);
// 5. 删除订单
$deleteResponse = $this->delete("/api/orders/{$orderId}",
TestHelper::authHeader($token));
$this->assertStatus($deleteResponse, 200);
}
}
配置文件
<?php
// phpunit.xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_HOST" value="127.0.0.1"/>
<env name="DB_NAME" value="test_database"/>
<env name="DB_USER" value="root"/>
<env name="DB_PASS" value=""/>
<env name="DB_PORT" value="3306"/>
</php>
</phpunit>
这个测试框架提供了完整的模拟请求和断言功能,可以根据项目需求灵活使用,建议在开发过程中持续编写和维护测试,确保代码质量和功能稳定性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播