PHP集成测试实践:使用Cypress或Puppeteer实现前后端(E2E)自动化测试

PHP集成测试实践:使用Cypress或Puppeteer实现前后端(E2E)自动化测试

大家好,今天我们来聊聊PHP集成测试,更具体地说,是如何利用Cypress或Puppeteer来实现前后端自动化测试,也就是端到端(E2E)测试。集成测试是介于单元测试和系统测试之间的一种测试类型,它验证应用的不同组件协同工作是否符合预期。E2E测试则更进一步,它模拟真实用户的使用场景,从用户界面开始,一直到后端数据存储,验证整个流程的正确性。

为什么需要E2E测试?

  • 覆盖范围广: E2E测试覆盖了整个应用,能发现单元测试和集成测试可能遗漏的问题,特别是那些组件之间交互导致的问题。
  • 更贴近用户: 它模拟用户的真实行为,能更好地评估用户体验。
  • 提高信心: 通过E2E测试,我们可以更有信心应用在发布后能正常工作。
  • 减少手动测试成本: 自动化E2E测试可以显著减少手动测试的工作量,提高测试效率。

Cypress vs. Puppeteer: 选择合适的工具

在选择E2E测试工具时,Cypress和Puppeteer是两个非常流行的选择。它们各有优缺点,适用于不同的场景。

特性 Cypress Puppeteer
技术栈 JavaScript, 与前端框架紧密集成 JavaScript, 可以控制Chrome/Chromium
测试体验 易于上手,调试方便,时间旅行 更灵活,可以模拟各种浏览器行为
性能 针对前端优化,速度快 性能取决于浏览器实例
API 简洁易懂,内置断言库 功能强大,需要自己处理断言
适用场景 前端为主的应用,需要快速迭代和调试 需要更精细的控制,例如性能测试
社区支持 活跃,文档完善 同样活跃,但可能更偏向底层控制

简单来说,如果你的项目主要关注前端交互,需要快速迭代和调试,Cypress是一个不错的选择。如果需要更底层、更灵活的控制,或者需要模拟各种复杂的浏览器行为,Puppeteer可能更适合。

在本次分享中,我们会分别使用Cypress和Puppeteer来演示如何进行PHP应用的E2E测试。为了演示,我们先搭建一个简单的PHP应用。

搭建一个简单的PHP应用

我们创建一个简单的PHP应用,包含一个登录页面和一个欢迎页面。

index.php (登录页面)

<?php
session_start();

if (isset($_SESSION['username'])) {
    header("Location: welcome.php");
    exit();
}

$error = '';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $username = $_POST['username'];
    $password = $_POST['password'];

    if ($username == 'test' && $password == 'password') {
        $_SESSION['username'] = $username;
        header("Location: welcome.php");
        exit();
    } else {
        $error = "Invalid username or password.";
    }
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    <?php if ($error): ?>
        <p style="color: red;"><?php echo $error; ?></p>
    <?php endif; ?>
    <form method="post">
        <label for="username">Username:</label><br>
        <input type="text" id="username" name="username"><br><br>
        <label for="password">Password:</label><br>
        <input type="password" id="password" name="password"><br><br>
        <input type="submit" value="Login">
    </form>
</body>
</html>

welcome.php (欢迎页面)

<?php
session_start();

if (!isset($_SESSION['username'])) {
    header("Location: index.php");
    exit();
}

$username = $_SESSION['username'];
?>

<!DOCTYPE html>
<html>
<head>
    <title>Welcome</title>
</head>
<body>
    <h1>Welcome, <?php echo $username; ?>!</h1>
    <a href="logout.php">Logout</a>
</body>
</html>

logout.php (登出页面)

<?php
session_start();
session_destroy();
header("Location: index.php");
exit();
?>

将这些文件放在你的PHP服务器的根目录下,例如 htdocs (如果使用XAMPP) 或者 public (如果使用Laravel)。

使用Cypress进行E2E测试

  1. 安装Cypress:

    首先,确保你已经安装了Node.js和npm。然后,在你的项目目录下运行以下命令安装Cypress:

    npm install cypress --save-dev
  2. 配置Cypress:

    安装完成后,运行以下命令打开Cypress测试运行器:

    npx cypress open

    Cypress会自动生成一些示例测试文件和配置文件。你可以在 cypress.config.js 文件中进行全局配置,例如设置baseUrl:

    const { defineConfig } = require("cypress");
    
    module.exports = defineConfig({
     e2e: {
       baseUrl: 'http://localhost', // 你的PHP应用的URL
       setupNodeEvents(on, config) {
         // implement node event listeners here
       },
     },
    });
  3. 创建测试用例:

    cypress/e2e 目录下创建一个新的测试文件,例如 login.cy.js

    describe('Login Test', () => {
       it('Should successfully login', () => {
           cy.visit('/'); // 访问登录页面
           cy.get('input[name="username"]').type('test'); // 输入用户名
           cy.get('input[name="password"]').type('password'); // 输入密码
           cy.get('input[type="submit"]').click(); // 点击登录按钮
           cy.url().should('include', '/welcome.php'); // 验证跳转到欢迎页面
           cy.contains('Welcome, test!').should('be.visible'); // 验证欢迎信息
       });
    
       it('Should display error message for invalid credentials', () => {
           cy.visit('/');
           cy.get('input[name="username"]').type('invalid');
           cy.get('input[name="password"]').type('invalid');
           cy.get('input[type="submit"]').click();
           cy.contains('Invalid username or password.').should('be.visible'); // 验证错误信息
       });
    
       it('Should successfully logout', () => {
           cy.visit('/');
           cy.get('input[name="username"]').type('test');
           cy.get('input[name="password"]').type('password');
           cy.get('input[type="submit"]').click();
           cy.get('a[href="logout.php"]').click(); // 点击登出链接
           cy.url().should('include', '/index.php'); // 验证跳转回登录页面
       });
    });

    这个测试用例包含了三个测试场景:

    • 成功登录
    • 无效凭证登录失败
    • 成功登出
  4. 运行测试:

    在Cypress测试运行器中,选择 login.cy.js 文件,Cypress会自动运行测试用例。你可以在Cypress的界面中看到测试过程的每一步,方便调试。

使用Puppeteer进行E2E测试

  1. 安装Puppeteer:

    在你的项目目录下运行以下命令安装Puppeteer:

    npm install puppeteer --save-dev
  2. 创建测试用例:

    创建一个新的测试文件,例如 login.test.js

    const puppeteer = require('puppeteer');
    
    describe('Login Test', () => {
       let browser;
       let page;
    
       beforeAll(async () => {
           browser = await puppeteer.launch();
           page = await browser.newPage();
       });
    
       afterAll(async () => {
           await browser.close();
       });
    
       it('Should successfully login', async () => {
           await page.goto('http://localhost/'); // 访问登录页面
           await page.type('input[name="username"]', 'test'); // 输入用户名
           await page.type('input[name="password"]', 'password'); // 输入密码
           await page.click('input[type="submit"]'); // 点击登录按钮
           await page.waitForNavigation(); // 等待页面跳转
           expect(page.url()).toContain('/welcome.php'); // 验证跳转到欢迎页面
           const welcomeText = await page.$eval('h1', el => el.textContent);
           expect(welcomeText).toBe('Welcome, test!'); // 验证欢迎信息
       });
    
       it('Should display error message for invalid credentials', async () => {
           await page.goto('http://localhost/');
           await page.type('input[name="username"]', 'invalid');
           await page.type('input[name="password"]', 'invalid');
           await page.click('input[type="submit"]');
           const errorText = await page.$eval('p[style="color: red;"]', el => el.textContent);
           expect(errorText).toBe('Invalid username or password.'); // 验证错误信息
       });
    
       it('Should successfully logout', async () => {
           await page.goto('http://localhost/');
           await page.type('input[name="username"]', 'test');
           await page.type('input[name="password"]', 'password');
           await page.click('input[type="submit"]');
           await page.waitForNavigation();
           await page.click('a[href="logout.php"]'); // 点击登出链接
           await page.waitForNavigation();
           expect(page.url()).toContain('/index.php'); // 验证跳转回登录页面
       });
    });

    这个测试用例与Cypress的测试用例类似,也包含了三个测试场景。不同的是,Puppeteer需要手动管理浏览器实例和页面,并且需要使用 expect 函数进行断言。

  3. 运行测试:

    你需要安装一个测试运行器,例如 Jest 或 Mocha。 这里以Jest为例:

    npm install jest --save-dev

    然后在 package.json 文件中添加一个测试脚本:

    {
       "scripts": {
           "test": "jest"
       }
    }

    最后,运行以下命令运行测试:

    npm test

集成测试中的PHP后端验证

E2E测试不仅仅是验证前端行为,还可以验证后端逻辑和数据。例如,我们可以验证用户登录后,session是否正确设置,或者登出后,session是否被销毁。

虽然Cypress和Puppeteer主要用于控制浏览器,但我们仍然可以通过它们发送HTTP请求到后端,并验证响应结果。

Cypress:

it('Should successfully login and verify session', () => {
    cy.visit('/');
    cy.get('input[name="username"]').type('test');
    cy.get('input[name="password"]').type('password');
    cy.get('input[type="submit"]').click();
    cy.url().should('include', '/welcome.php');
    cy.contains('Welcome, test!').should('be.visible');

    // 发送请求到后端,验证session
    cy.request('/welcome.php')
      .then((response) => {
        expect(response.status).to.eq(200);
        expect(response.body).to.include('Welcome, test!'); // 验证响应内容包含用户名
      });
});

Puppeteer:

it('Should successfully login and verify session', async () => {
    await page.goto('http://localhost/');
    await page.type('input[name="username"]', 'test');
    await page.type('input[name="password"]', 'password');
    await page.click('input[type="submit"]');
    await page.waitForNavigation();
    expect(page.url()).toContain('/welcome.php');
    const welcomeText = await page.$eval('h1', el => el.textContent);
    expect(welcomeText).toBe('Welcome, test!');

    // 发送请求到后端,验证session
    const response = await page.goto('http://localhost/welcome.php');
    expect(response.status()).toBe(200);
    const body = await response.text();
    expect(body).toContain('Welcome, test!'); // 验证响应内容包含用户名
});

在这个例子中,我们使用 cy.request (Cypress) 和 page.goto (Puppeteer) 发送HTTP请求到 /welcome.php,然后验证响应状态码和响应内容。

E2E测试的最佳实践

  • 保持测试用例的独立性: 每个测试用例应该独立运行,不依赖于其他测试用例的状态。
  • 使用清晰的测试用例名称: 测试用例名称应该清晰地描述测试的目的,方便理解和维护。
  • 编写可维护的测试代码: 使用良好的代码风格和注释,方便他人阅读和修改。
  • 定期运行测试: 将E2E测试集成到CI/CD流程中,定期运行测试,及时发现问题。
  • 关注测试速度: 尽量减少不必要的等待时间,提高测试效率。
  • Mock外部依赖: 对于外部服务或API,可以使用Mock技术模拟它们的行为,避免依赖外部环境。

总结一下

通过本次分享,我们了解了E2E测试的重要性,以及如何使用Cypress和Puppeteer来实现PHP应用的E2E测试。 无论是选择Cypress还是Puppeteer, 关键在于理解它们的优缺点,并根据项目的实际需求做出选择。 编写高质量的E2E测试用例,可以帮助我们发现潜在的问题,提高应用质量,最终提升用户体验。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注