本文目录导读:

我来详细介绍 PHP 端到端测试的编写方法,涵盖主流工具和最佳实践。
选择 E2E 测试工具
主流工具对比
// 工具选择 1. Codeception - PHP 原生,支持多种框架 2. Laravel Dusk - Laravel 专用,简洁易用 3. Playwright - 跨语言,功能强大 4. Selenium - 老牌工具,生态成熟
使用 Laravel Dusk 编写 E2E 测试
安装配置
# 安装 Dusk composer require laravel/dusk --dev php artisan dusk:install # ChromeDriver 安装 php artisan dusk:chrome-driver
编写基础测试
<?php
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class UserLoginTest extends DuskTestCase
{
/**
* 测试用户登录流程
*/
public function testUserCanLogin()
{
$this->browse(function (Browser $browser) {
$browser->visit('/login')
->type('email', 'test@example.com')
->type('password', 'password123')
->press('登录')
->assertPathIs('/dashboard')
->assertSee('欢迎回来');
});
}
/**
* 测试注册流程
*/
public function testUserCanRegister()
{
$this->browse(function (Browser $browser) {
$browser->visit('/register')
->type('name', '测试用户')
->type('email', 'newuser@example.com')
->type('password', 'secret123')
->type('password_confirmation', 'secret123')
->press('注册')
->assertPathIs('/home')
->assertSee('注册成功');
});
}
}
使用 Codeception 编写 E2E 测试
配置 Codeception
# codeception.yml
actor: Tester
paths:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
extensions:
enabled:
- Codeception\Extension\RunFailed
modules:
config:
WebDriver:
url: 'http://localhost:8000'
browser: chrome
window_size: 1920x1080
Laravel:
environment_file: .env.testing
cleanup: true
编写 E2E 测试
<?php
class CheckoutCest
{
/**
* 测试完整购物流程
*/
public function testCompleteCheckout(AcceptanceTester $I)
{
// 访问商品页
$I->amOnPage('/products');
$I->see('商品列表');
// 选择商品
$I->click('加入购物车');
$I->see('购物车已更新');
// 去结算
$I->click('去结算');
$I->seeCurrentUrlEquals('/checkout');
// 填写收货信息
$I->fillField('收货人', '张三');
$I->fillField('联系电话', '13800138000');
$I->fillField('收货地址', '北京市朝阳区');
// 提交订单
$I->click('提交订单');
// 验证结果
$I->see('订单提交成功');
$I->see('订单号');
}
}
复杂场景测试
多页面流程测试
<?php
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class OrderFlowTest extends DuskTestCase
{
/**
* 完整订单流程测试
*/
public function testCompleteOrderFlow()
{
$this->browse(function (Browser $browser) {
// 登录
$browser->visit('/login')
->type('email', 'user@example.com')
->type('password', 'password')
->press('登录')
// 浏览商品
->visit('/products')
->click('#product-1')
->assertSee('商品详情')
// 加入购物车
->press('加入购物车')
->click('#cart-link')
->assertSee('购物车')
// 确认订单
->press('去结算')
->assertPathIs('/checkout')
// 填写地址
->type('address', '北京市朝阳区测试路1号')
->type('phone', '13800138000')
// 选择支付方式
->radio('payment', 'wechat')
->press('确认支付')
// 验证成功
->assertPathIs('/order/success')
->assertSee('支付成功');
});
}
/**
* 测试搜索功能
*/
public function testProductSearch()
{
$this->browse(function (Browser $first, Browser $second) {
// 多浏览器会话测试
$first->visit('/')
->type('#search', '手机')
->press('搜索')
->assertSee('搜索结果');
$second->visit('/')
->type('#search', '电脑')
->press('搜索')
->assertSee('搜索结果');
});
}
}
数据准备与清理
使用 Traits 管理测试数据
<?php
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
use App\Models\User;
use App\Models\Product;
trait PreparationTrait
{
/**
* 创建测试数据
*/
protected function setUpTestData()
{
// 创建用户
$user = User::create([
'name' => '测试用户',
'email' => 'test@example.com',
'password' => bcrypt('password123')
]);
// 创建商品
$product = Product::create([
'name' => '测试商品',
'price' => 99.99,
'stock' => 100
]);
return [
'user' => $user,
'product' => $product
];
}
/**
* 清理测试数据
*/
protected function cleanupTestData($data)
{
if (isset($data['user'])) {
$data['user']->delete();
}
if (isset($data['product'])) {
$data['product']->delete();
}
}
}
class ProductManagementTest extends DuskTestCase
{
use PreparationTrait;
public function testAdminCanCreateProduct()
{
$data = $this->setUpTestData();
$this->browse(function (Browser $browser) use ($data) {
$browser->loginAs($data['user'])
->visit('/admin/products')
->press('新增商品')
->type('name', '新商品')
->type('price', '199.99')
->type('stock', '50')
->press('保存')
->assertSee('商品创建成功')
->assertSee('新商品');
});
$this->cleanupTestData($data);
}
}
处理异步操作
<?php
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class AsyncTest extends DuskTestCase
{
/**
* 测试 AJAX 请求
*/
public function testAjaxRequest()
{
$this->browse(function (Browser $browser) {
$browser->visit('/products')
// 等待异步加载
->waitFor('.product-item', 10)
->script("document.querySelector('#load-more').click()")
->waitFor('.product-item:nth-child(11)', 10)
->assertSee('第11个商品');
});
}
/**
* 测试实时更新
*/
public function testRealTimeUpdates()
{
$this->browse(function (Browser $browser) {
$browser->visit('/dashboard')
->waitForText('实时数据', 5)
->assertSee('连接状态:在线')
// 等待 WebSocket 消息
->waitFor(function () use ($browser) {
return $browser->element('.notification')
->getText() !== '';
}, 10)
->assertVisible('.notification');
});
}
}
测试组织和最佳实践
<?php
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class E2EBestPractices extends DuskTestCase
{
/**
* 断言使用
*/
public function testAssertions()
{
$this->browse(function (Browser $browser) {
$browser->visit('/page')
// 基本断言
->assertVisible('.main-content')
->assertHidden('.loading')
->assertDisabled('#submit-btn')
->assertEnabled('#submit-btn')
->assertChecked('#agree')
->assertNotChecked('#newsletter')
// 文本断言
->assertSeeIn('.header', '页面标题')
->assertSeeLink('了解更多')
// URL 断言
->assertQueryStringHas('sort', 'desc')
->assertQueryStringMissing('page');
});
}
/**
* 等待策略
*/
public function testWaitingStrategies()
{
$this->browse(function (Browser $browser) {
$browser->visit('/slow-page')
// 等待元素出现
->waitFor('#content', 10)
// 等待文本
->waitForText('加载完成', 10)
// 等待元素消失
->waitUntilMissing('.loading', 10)
// 自定义等待
->waitUntil(function () use ($browser) {
return $browser->script("return document.querySelector('#status').innerText == 'ready'");
}, 10)
// 暂停调试
->pause(1000);
});
}
/**
* 截图和调试
*/
public function testDebugging()
{
$this->browse(function (Browser $browser) {
$browser->visit('/page')
// 保存截图
->screenshot('page-'.time())
// 下载响应
->storeSource('page-source-'.time())
// 控制台日志
->driver->manage()->getLog('browser');
});
}
}
运行和配置
{
"scripts": {
"test:e2e": "php artisan dusk",
"test:e2e:filter": "php artisan dusk --filter=testMethodName",
"test:e2e:parallel": "php artisan dusk --parallel"
},
"dusk": {
"port": 9515,
"env": {
"APP_ENV": "testing",
"APP_URL": "http://localhost:8000"
}
}
}
# 常用命令 php artisan dusk # 运行所有 E2E 测试 php artisan dusk --filter=login # 运行指定测试 php artisan dusk:chrome-driver # 更新 ChromeDriver php artisan dusk:install # 安装 Dusk
关键要点
- 独立环境:使用专门的测试数据库和环境
- 数据隔离:每个测试用例使用独立的数据
- 等待策略:合理处理异步操作
- 断言完整:验证页面状态、URL、数据完整性
- 错误处理:测试失败时保存截图和现场信息
- 性能优化:并行运行测试,减少等待时间
通过合理使用这些工具和方法,可以构建可靠的 PHP 端到端测试套件,确保应用的核心功能稳定运行。