PHP 怎么端到端测试编写

wen PHP项目 3

本文目录导读:

PHP 怎么端到端测试编写

  1. 选择 E2E 测试工具
  2. 使用 Laravel Dusk 编写 E2E 测试
  3. 使用 Codeception 编写 E2E 测试
  4. 复杂场景测试
  5. 数据准备与清理
  6. 处理异步操作
  7. 测试组织和最佳实践
  8. 运行和配置
  9. 关键要点

我来详细介绍 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

关键要点

  1. 独立环境:使用专门的测试数据库和环境
  2. 数据隔离:每个测试用例使用独立的数据
  3. 等待策略:合理处理异步操作
  4. 断言完整:验证页面状态、URL、数据完整性
  5. 错误处理:测试失败时保存截图和现场信息
  6. 性能优化:并行运行测试,减少等待时间

通过合理使用这些工具和方法,可以构建可靠的 PHP 端到端测试套件,确保应用的核心功能稳定运行。

抱歉,评论功能暂时关闭!