chatbot-ui自动化测试:端到端测试用例编写

chatbot-ui自动化测试:端到端测试用例编写

【免费下载链接】chatbot-ui chatbot-ui - 一个开源的 AI 模型聊天界面,可以轻松地与 OpenAI 的 API 集成,用于构建聊天机器人。 【免费下载链接】chatbot-ui 项目地址: https://gitcode.com/GitHub_Trending/ch/chatbot-ui

概述

在现代Web应用开发中,自动化测试已成为确保软件质量的关键环节。chatbot-ui作为一个开源的AI聊天界面项目,采用Playwright作为端到端测试框架,为开发者提供了完善的测试基础设施。本文将深入探讨如何为chatbot-ui编写高质量的端到端测试用例。

测试框架配置

Playwright基础配置

chatbot-ui使用Playwright进行端到端测试,配置文件位于__tests__/playwright-test/playwright.config.ts

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {
    trace: 'on-first-retry',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

测试脚本配置

package.json中定义了测试相关脚本:

{
  "scripts": {
    "integration": "playwright test",
    "integration:open": "playwright test --ui",
    "integration:codegen": "playwright codegen"
  }
}

核心测试用例编写

登录功能测试

chatbot-ui的认证系统基于Supabase Auth,以下是完整的登录功能测试用例:

import { test, expect } from '@playwright/test';

// 测试用例:开始聊天按钮显示
test('start chatting is displayed', async ({ page }) => {
  await page.goto('http://localhost:3000/');
  await expect(page.getByRole('link', { name: 'Start Chatting' })).toBeVisible();
});

// 测试用例:无密码登录错误处理
test('No password error message', async ({ page }) => {
  await page.goto('http://localhost:3000/login');
  await page.getByPlaceholder('you@example.com').fill('dummyemail@gmail.com');
  await page.getByRole('button', { name: 'Login' }).click();
  await page.waitForLoadState('networkidle');
  await expect(page.getByText('Invalid login credentials')).toBeVisible();
});

// 测试用例:注册时缺少密码验证
test('No password for signup', async ({ page }) => {
  await page.goto('http://localhost:3000/login');
  await page.getByPlaceholder('you@example.com').fill('dummyEmail@Gmail.com');
  await page.getByRole('button', { name: 'Sign Up' }).click();
  await expect(page.getByText('Signup requires a valid')).toBeVisible();
});

// 测试用例:无效用户名注册验证
test('invalid username for signup', async ({ page }) => {
  await page.goto('http://localhost:3000/login');
  await page.getByPlaceholder('you@example.com').fill('dummyEmail');
  await page.getByPlaceholder('••••••••').fill('dummypassword');
  await page.getByRole('button', { name: 'Sign Up' }).click();
  await expect(page.getByText('Unable to validate email')).toBeVisible();
});

// 测试用例:密码重置功能验证
test('password reset message', async ({ page }) => {
  await page.goto('http://localhost:3000/login');
  await page.getByPlaceholder('you@example.com').fill('demo@gmail.com');
  await page.getByRole('button', { name: 'Reset' }).click();
  await expect(page.getByText('Check email to reset password')).toBeVisible();
});

测试用例设计模式

页面对象模式(Page Object Pattern)

为了提高测试代码的可维护性,建议使用页面对象模式:

// pages/login.page.ts
export class LoginPage {
  constructor(private page: Page) {}

  async navigate() {
    await this.page.goto('http://localhost:3000/login');
  }

  async fillEmail(email: string) {
    await this.page.getByPlaceholder('you@example.com').fill(email);
  }

  async fillPassword(password: string) {
    await this.page.getByPlaceholder('••••••••').fill(password);
  }

  async clickLogin() {
    await this.page.getByRole('button', { name: 'Login' }).click();
  }

  async clickSignUp() {
    await this.page.getByRole('button', { name: 'Sign Up' }).click();
  }

  async clickReset() {
    await this.page.getByRole('button', { name: 'Reset' }).click();
  }

  async getErrorMessage() {
    return this.page.getByText('Invalid login credentials');
  }
}

// 使用页面对象的测试用例
test('login with page object', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.navigate();
  await loginPage.fillEmail('test@example.com');
  await loginPage.clickLogin();
  await expect(loginPage.getErrorMessage()).toBeVisible();
});

数据驱动测试

使用数据驱动的方式提高测试覆盖率:

const loginTestData = [
  { email: '', password: 'password123', expectedError: 'Email is required' },
  { email: 'invalid-email', password: 'password123', expectedError: 'Invalid email format' },
  { email: 'test@example.com', password: '', expectedError: 'Password is required' },
];

loginTestData.forEach((data, index) => {
  test(`login validation test ${index + 1}`, async ({ page }) => {
    await page.goto('http://localhost:3000/login');
    await page.getByPlaceholder('you@example.com').fill(data.email);
    await page.getByPlaceholder('••••••••').fill(data.password);
    await page.getByRole('button', { name: 'Login' }).click();
    await expect(page.getByText(data.expectedError)).toBeVisible();
  });
});

聊天功能测试用例

基础聊天交互测试

test('chat message sending and receiving', async ({ page }) => {
  // 登录并进入聊天界面
  await loginWithCredentials(page, 'test@example.com', 'password123');
  
  // 输入消息并发送
  await page.getByPlaceholder('Message Chatbot UI...').fill('Hello, how are you?');
  await page.getByRole('button', { name: 'Send message' }).click();
  
  // 验证消息发送成功
  await expect(page.getByText('Hello, how are you?')).toBeVisible();
  
  // 等待AI响应(模拟或真实API)
  await expect(page.getByText(/I'm doing well/)).toBeVisible({ timeout: 10000 });
});

test('chat history persistence', async ({ page }) => {
  // 发送多条消息
  await loginWithCredentials(page, 'test@example.com', 'password123');
  
  const messages = ['Message 1', 'Message 2', 'Message 3'];
  for (const message of messages) {
    await page.getByPlaceholder('Message Chatbot UI...').fill(message);
    await page.getByRole('button', { name: 'Send message' }).click();
    await expect(page.getByText(message)).toBeVisible();
  }
  
  // 刷新页面验证历史记录持久化
  await page.reload();
  for (const message of messages) {
    await expect(page.getByText(message)).toBeVisible();
  }
});

文件上传功能测试

test('file upload in chat', async ({ page }) => {
  await loginWithCredentials(page, 'test@example.com', 'password123');
  
  // 点击文件上传按钮
  await page.getByRole('button', { name: 'Upload file' }).click();
  
  // 选择文件(使用Playwright的文件上传功能)
  const fileInput = page.locator('input[type="file"]');
  await fileInput.setInputFiles('path/to/test-file.txt');
  
  // 验证文件上传成功
  await expect(page.getByText('test-file.txt')).toBeVisible();
  await expect(page.getByText('File uploaded successfully')).toBeVisible();
});

高级测试场景

多浏览器兼容性测试

// 在playwright.config.ts中配置多浏览器项目
projects: [
  {
    name: 'chromium',
    use: { ...devices['Desktop Chrome'] },
  },
  {
    name: 'firefox', 
    use: { ...devices['Desktop Firefox'] },
  },
  {
    name: 'webkit',
    use: { ...devices['Desktop Safari'] },
  },
  {
    name: 'Mobile Chrome',
    use: { ...devices['Pixel 5'] },
  },
  {
    name: 'Mobile Safari',
    use: { ...devices['iPhone 12'] },
  },
]

// 响应式设计测试
test('responsive design on mobile', async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 812 }); // iPhone X尺寸
  
  await page.goto('http://localhost:3000/login');
  
  // 验证移动端布局
  await expect(page.getByRole('button', { name: 'Menu' })).toBeVisible();
  await expect(page.locator('.sidebar')).toBeHidden(); // 侧边栏应隐藏
});

性能测试集成

test('chat response time performance', async ({ page }) => {
  await loginWithCredentials(page, 'test@example.com', 'password123');
  
  const startTime = Date.now();
  
  await page.getByPlaceholder('Message Chatbot UI...').fill('Test performance');
  await page.getByRole('button', { name: 'Send message' }).click();
  
  // 等待AI响应并测量时间
  await page.waitForResponse(response => 
    response.url().includes('/api/chat') && response.status() === 200
  );
  
  const responseTime = Date.now() - startTime;
  
  // 性能断言:响应时间应小于2秒
  expect(responseTime).toBeLessThan(2000);
  
  console.log(`Chat response time: ${responseTime}ms`);
});

测试最佳实践

1. 测试数据管理

// test-data.ts
export const TestUsers = {
  admin: { email: 'admin@example.com', password: 'admin123' },
  user: { email: 'user@example.com', password: 'user123' },
  guest: { email: 'guest@example.com', password: 'guest123' },
};

export const TestMessages = {
  simple: 'Hello, world!',
  long: 'This is a longer message to test message length handling and UI responsiveness.',
  specialChars: 'Message with special chars: !@#$%^&*()_+{}|:"<>?[]\\;\',./`~'
};

2. 测试钩子和清理

// 全局设置和清理
test.beforeAll(async ({ browser }) => {
  // 创建测试用户等准备工作
});

test.afterAll(async () => {
  // 清理测试数据
});

test.beforeEach(async ({ page }) => {
  // 每个测试前的准备工作
  await page.context().clearCookies();
});

test.afterEach(async ({ page }, testInfo) => {
  // 测试失败时截图
  if (testInfo.status !== testInfo.expectedStatus) {
    const screenshotPath = testInfo.outputPath(`failure-${testInfo.title}.png`);
    await page.screenshot({ path: screenshotPath });
  }
});

3. 环境变量配置

// 使用环境变量控制测试行为
const BASE_URL = process.env.TEST_BASE_URL || 'http://localhost:3000';
const IS_CI = process.env.CI === 'true';

test('environment aware test', async ({ page }) => {
  await page.goto(`${BASE_URL}/login`);
  
  if (IS_CI) {
    // CI环境特定的测试逻辑
    test.slow(); // 标记为慢测试
  }
  
  // 通用测试逻辑
});

测试报告和CI集成

HTML测试报告

Playwright默认生成HTML报告,位于playwright-report目录。报告包含:

  • 测试执行详情
  • 失败测试的截图和录屏
  • 性能指标和时间线
  • 浏览器兼容性信息

CI/CD流水线集成

# GitHub Actions示例
name: Playwright Tests
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: 18
    - name: Install dependencies
      run: npm ci
    - name: Install Playwright browsers
      run: npx playwright install --with-deps
    - name: Run Playwright tests
      run: npx playwright test
    - uses: actions/upload-artifact@v4
      if: always()
      with:
        name: playwright-report
        path: playwright-report/
        retention-days: 30

常见问题排查

1. 元素定位问题

// 使用更稳定的定位策略
// 不推荐:基于文本内容定位
await page.click('text=Login');

// 推荐:使用角色和名称定位
await page.getByRole('button', { name: 'Login' }).click();

// 或者使用测试ID
await page.getByTestId('login-button').click();

2. 异步操作处理

// 错误的等待方式
await page.waitForTimeout(5000); // 固定等待,不推荐

// 推荐的等待方式
await page.waitForLoadState('networkidle'); // 等待网络空闲
await page.waitForSelector('.chat-message'); // 等待特定元素
await page.waitForResponse('/api/chat'); // 等待API响应

3. 跨浏览器兼容性

// 处理浏览器差异
test('cross-browser compatibility', async ({ page, browserName }) => {
  await page.goto('http://localhost:3000/login');
  
  if (browserName === 'firefox') {
    // Firefox特定的处理逻辑
    await page.waitForTimeout(1000); // Firefox可能需要额外等待
  }
  
  // 通用测试逻辑
});

总结

通过本文的详细指导,您已经掌握了为chatbot-ui项目编写高质量端到端测试用例的核心技能。从基础配置到高级场景,从最佳实践到常见问题排查,这些知识将帮助您构建稳定可靠的自动化测试套件。

记住优秀的测试用例应该具备:

  • 清晰的测试目的和预期结果
  • 稳定的元素定位策略
  • 适当的等待和异步处理

【免费下载链接】chatbot-ui chatbot-ui - 一个开源的 AI 模型聊天界面,可以轻松地与 OpenAI 的 API 集成,用于构建聊天机器人。 【免费下载链接】chatbot-ui 项目地址: https://gitcode.com/GitHub_Trending/ch/chatbot-ui

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值