AI 代码审查员:前端测试也能“全自动”

引言

在现代前端开发中,测试和调试是确保代码质量和用户体验的关键环节。随着 Web 应用的复杂性不断增加,手动编写测试用例和调试代码变得耗时且容易出错。人工智能(AI)技术的进步为我们提供了全新的解决方案:通过大型语言模型(LLM)如 GPT,开发者可以自动生成测试用例、模拟 API 响应、分析错误日志,甚至优化测试覆盖率。这种 AI 驱动的测试方法不仅提升了效率,还为前端开发带来了革命性的变化。

本文将深入探讨如何利用 GPT 实现前端测试的自动化,涵盖单元测试、端到端测试、API 模拟和错误分析。我们将结合 Jest、Playwright 和 Cypress 等流行测试框架,展示如何将 AI 与传统测试工具集成,构建高效的测试工作流。同时,我们将通过一个完整的案例——测试一个用户管理页面,展示 AI 在实际项目中的应用,并提供一个练习,帮助您将理论转化为实践。本文特别推荐 Cypress 的调试体验,展示其如何助力高效测试开发。

通过本项目,您将体验到:

  • 提示词设计:如何编写清晰的提示词,让 GPT 理解组件行为。
  • 测试用例生成:使用 GPT 自动生成 Jest 和 Cypress 测试。
  • API 模拟:通过 GPT 生成模拟数据,减少后端依赖。
  • 错误分析:利用 GPT 解析多语言错误日志并提供修复建议。
  • 自动化集成:将 AI 生成的测试融入 CI/CD 流程。
  • 性能优化:通过 AI 分析测试覆盖率和性能瓶颈。

需求分析

在动手编码之前,我们需要明确项目的功能需求。一个清晰的需求清单不仅能指导开发过程,还能帮助我们理解每个功能的意义。以下是 AI 驱动测试项目的核心需求:

  1. 自动生成测试用例
    • 支持生成单元测试(Jest)和端到端测试(Cypress/Playwright)。
    • 覆盖组件的常见场景和边界情况(如空数据、异常输入)。
  2. API 模拟
    • 生成模拟 API 响应,减少对后端依赖。
    • 支持动态生成复杂数据结构(如嵌套对象、数组)。
  3. 错误分析
    • 解析多语言错误日志(支持中文、英文等),提供修复建议。
    • 识别常见前端错误,如空指针、类型错误等。
  4. 自动化测试集成
    • 将 AI 生成的测试融入 CI/CD 流程。
    • 支持 GitHub Actions 等工具,实现自动触发测试。
  5. 用户体验
    • 提供直观的测试结果反馈,包括通过/失败状态和覆盖率。
    • 支持调试过程中的交互式分析(如错误定位)。
  6. 性能优化
    • 分析测试覆盖率,识别未覆盖的代码分支。
    • 优化测试执行时间和资源消耗。

需求背后的意义

这些需求覆盖了 AI 驱动测试的核心场景,同时为学习 React 和 AI 技术提供了丰富的实践机会:

  • 测试用例生成:自动化减少手动编写的工作量,提高开发效率。
  • API 模拟:隔离组件测试,降低对后端环境的依赖。
  • 错误分析:简化调试流程,降低技术门槛。
  • 自动化集成:确保测试流程的可持续性和一致性。
  • 用户体验:通过直观反馈提升开发者的测试体验。
  • 性能优化:确保测试覆盖率和执行效率,适合大型项目。

技术栈选择

在实现功能之前,我们需要选择合适的技术栈。以下是本项目使用的工具和技术,以及选择它们的理由:

  • React
    核心前端框架,用于构建测试用例展示界面。React 的组件化特性适合快速开发动态 UI。
  • Jest
    流行的单元测试框架,与 React 集成紧密,支持快照测试和 mock 功能。
  • Cypress
    端到端测试框架,提供卓越的调试体验,适合快速迭代和交互式测试。
  • Playwright
    支持多浏览器测试(如 Chrome、Firefox、Safari),适合跨平台验证。
  • OpenAI GPT
    用于生成测试用例、模拟 API 数据和分析错误,提供强大的自然语言处理能力。
  • Vite
    构建工具,提供快速的开发服务器和高效的打包能力,符合 2025 年高性能开发趋势。
  • Tailwind CSS
    提供灵活的样式解决方案,支持响应式设计,简化 UI 开发。
  • GitHub Actions
    用于自动化测试和 CI/CD 集成,支持持续交付。
  • Vercel
    用于部署应用,提供高可用性和全球 CDN 支持,与 React 生态深度集成。

技术栈优势

  • React:生态丰富,社区活跃,适合快速开发。
  • Jest:支持快照测试和 mock 功能,适合单元测试。
  • Cypress:提供交互式调试界面,开发者体验优越。
  • Playwright:跨浏览器支持强大,适合复杂场景。
  • OpenAI GPT:强大的代码生成和错误分析能力,简化测试开发。
  • Vite:启动速度快,热更新体验优越。
  • Tailwind CSS:简化样式开发,提升 UI 一致性。
  • GitHub Actions:支持自动化测试流程,确保代码质量。
  • Vercel:一键部署,全球 CDN 加速。

项目实现

现在进入核心部分——代码实现。我们将从项目搭建开始,逐步完成组件设计、测试用例生成、API 模拟、错误分析和自动化测试集成。

1. 项目搭建

使用 Vite 创建一个 React 项目:

npm create vite@latest ai-testing -- --template react
cd ai-testing
npm install
npm run dev

安装必要的依赖:

npm install @testing-library/react @testing-library/jest-dom cypress @playwright/test @tanstack/react-query tailwindcss postcss autoprefixer axios

初始化 Tailwind CSS:

npx tailwindcss init -p

编辑 tailwind.config.js

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

src/index.css 中引入 Tailwind:

@tailwind base;
@tailwind components;
@tailwind utilities;

配置 Jest:

npm init jest@latest

编辑 jest.config.js

module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
  },
};

创建 jest.setup.js

import '@testing-library/jest-dom';

配置 Cypress:

npx cypress open

编辑 cypress.config.js

const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    baseUrl: '[invalid url, do not cite]
    setupNodeEvents(on, config) {
      // 配置 Cypress
    },
  },
});

配置 Playwright:

npx playwright install

创建 playwright.config.js

const { defineConfig } = require('@playwright/test');

module.exports = defineConfig({
  testDir: './tests',
  use: {
    baseURL: '[invalid url, do not cite]
    browserName: 'chromium',
  },
});

2. 组件拆分

我们将构建一个展示测试用例和结果的界面,包含以下组件:

  • App:根组件,负责整体布局。
  • TestGenerator:生成测试用例的界面,接受用户输入的提示词。
  • TestRunner:运行测试并显示结果,支持 Jest 和 Cypress。
  • ErrorAnalyzer:分析错误日志,提供修复建议。
  • ResultDisplay:展示测试结果,包括通过/失败状态和覆盖率。
文件结构
src/
├── components/
│   ├── TestGenerator.tsx
│   ├── TestRunner.tsx
│   ├── ErrorAnalyzer.tsx
│   └── ResultDisplay.tsx
├── hooks/
│   └── useAI.ts
├── tests/
│   ├── UserList.test.tsx
│   └── e2e/
│       ├── userList.spec.js
├── App.tsx
├── main.tsx
└── index.css

3. 使用 GPT 生成测试用例

我们将通过 API 调用 GPT 生成测试用例,结合 Jest 和 Cypress。

配置 OpenAI API 后端

创建一个简单的 Node.js 后端服务调用 OpenAI API:

mkdir backend
cd backend
npm init -y
npm install express openai

backend/index.js

require('dotenv').config();
const express = require('express');
const { OpenAI } = require('openai');
const app = express();

app.use(express.json());

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

app.post('/api/generate-test', async (req, res) => {
  const { prompt, type } = req.body;
  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4',
      messages: [{ role: 'user', content: `生成 ${type} 测试用例:\n${prompt}` }],
    });
    res.json({ code: response.choices[0].message.content });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.post('/api/analyze-error', async (req, res) => {
  const { errorLog } = req.body;
  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4',
      messages: [{ role: 'user', content: `分析以下错误并提供解决方案:\n${errorLog}` }],
    });
    res.json({ analysis: response.choices[0].message.content });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3001, () => console.log('Server running on port 3001'));

创建 .env 文件:

OPENAI_API_KEY=your_openai_api_key

运行后端:

node index.js
TestGenerator 组件

src/components/TestGenerator.tsx

import { useState } from 'react';
import axios from 'axios';

interface TestGeneratorProps {
  onGenerate: (code: string, type: 'jest' | 'cypress' | 'playwright') => void;
}

function TestGenerator({ onGenerate }: TestGeneratorProps) {
  const [prompt, setPrompt] = useState('');
  const [type, setType] = useState<'jest' | 'cypress' | 'playwright'>('jest');
  const [isLoading, setIsLoading] = useState(false);

  const handleGenerate = async () => {
    setIsLoading(true);
    try {
      const response = await axios.post('[invalid url, do not cite]
        { prompt, type }
      );
      onGenerate(response.data.code, type);
    } catch (error) {
      console.error('生成测试用例失败:', error);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div className="p-4 bg-white rounded-lg shadow">
      <h2 className="text-xl font-bold mb-4">生成测试用例</h2>
      <select
        value={type}
        onChange={(e) => setType(e.target.value as 'jest' | 'cypress' | 'playwright')}
        className="mb-2 p-2 border rounded-lg"
      >
        <option value="jest">Jest (单元测试)</option>
        <option value="cypress">Cypress (端到端)</option>
        <option value="playwright">Playwright (端到端)</option>
      </select>
      <textarea
        value={prompt}
        onChange={(e) => setPrompt(e.target.value)}
        className="w-full p-2 border rounded-lg"
        placeholder="输入组件描述和测试场景..."
        rows={5}
      />
      <button
        onClick={handleGenerate}
        className="mt-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400"
        disabled={isLoading}
      >
        {isLoading ? '生成中...' : '生成测试'}
      </button>
    </div>
  );
}

export default TestGenerator;

4. 案例:测试用户管理页面

需求
  • 页面功能:显示用户列表,支持添加和删除用户。
  • 测试目标
    • 单元测试:验证列表渲染、添加和删除功能。
    • 端到端测试:验证用户交互和页面跳转。
    • API 模拟:生成用户数据。
    • 错误分析:处理 API 失败情况。
实现

src/components/UserList.tsx

import { useState, useEffect } from 'react';
import axios from 'axios';

interface User {
  id: number;
  name: string;
  email: string;
}

function UserList() {
  const [users, setUsers] = useState<User[]>([]);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    axios.get('[invalid url, do not cite]
      .then(response => setUsers(response.data))
      .catch(err => setError('获取用户失败: ' + err.message));
  }, []);

  const addUser = () => {
    const newUser = { id: Date.now(), name: '新用户', email: 'new@example.com' };
    setUsers([...users, newUser]);
  };

  const deleteUser = (id: number) => {
    setUsers(users.filter(user => user.id !== id));
  };

  if (error) return <div className="text-red-500">{error}</div>;

  return (
    <div className="p-4 bg-white rounded-lg shadow">
      <h2 className="text-xl font-bold mb-4">用户管理</h2>
      <button
        onClick={addUser}
        className="mb-4 px-4 py-2 bg-blue-600 text-white rounded-lg"
      >
        添加用户
      </button>
      <ul className="space-y-2">
        {users.map(user => (
          <li key={user.id} className="flex justify-between items-center">
            <span>{user.name} ({user.email})</span>
            <button
              onClick={() => deleteUser(user.id)}
              className="text-red-500"
            >
              删除
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default UserList;
单元测试

提示词:

为以下 React 组件编写 Jest 测试用例,使用 @testing-library/react:
- 组件名称:UserList
- 功能:显示用户列表,支持添加和删除用户
- 测试场景:
  1. 初始渲染时显示空列表
  2. 添加用户后列表更新
  3. 删除用户后列表更新
  4. API 调用失败显示错误消息

```js
import { useState, useEffect } from 'react';
import axios from 'axios';

interface User {
  id: number;
  name: string;
  email: string;
}

function UserList() {
  const [users, setUsers] = useState<User[]>([]);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    axios.get('[invalid url, do not cite]
      .then(response => setUsers(response.data))
      .catch(err => setError('获取用户失败: ' + err.message));
  }, []);

  const addUser = () => {
    const newUser = { id: Date.now(), name: '新用户', email: 'new@example.com' };
    setUsers([...users, newUser]);
  };

  const deleteUser = (id: number) => {
    setUsers(users.filter(user => user.id !== id));
  };

  if (error) return <div className="text-red-500">{error}</div>;

  return (
    <div className="p-4 bg-white rounded-lg shadow">
      <h2 className="text-xl font-bold mb-4">用户管理</h2>
      <button
        onClick={addUser}
        className="mb-4 px-4 py-2 bg-blue-600 text-white rounded-lg"
      >
        添加用户
      </button>
      <ul className="space-y-2">
        {users.map(user => (
          <li key={user.id} className="flex justify-between items-center">
            <span>{user.name} ({user.email})</span>
            <button
              onClick={() => deleteUser(user.id)}
              className="text-red-500"
            >
              删除
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default UserList;

GPT 生成的 Jest 测试:

```ts
import { render, screen, fireEvent } from '@testing-library/react';
import axios from 'axios';
import UserList from './UserList';

jest.mock('axios');

describe('UserList 组件', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  test('初始渲染时显示空列表', async () => {
    axios.get.mockResolvedValue({ data: [] });
    render(<UserList />);
    expect(screen.queryByRole('listitem')).not.toBeInTheDocument();
  });

  test('添加用户后列表更新', async () => {
    axios.get.mockResolvedValue({ data: [] });
    render(<UserList />);
    fireEvent.click(screen.getByText('添加用户'));
    expect(await screen.findByText('新用户')).toBeInTheDocument();
  });

  test('删除用户后列表更新', async () => {
    axios.get.mockResolvedValue({
      data: [{ id: 1, name: '张三', email: 'zhangsan@example.com' }],
    });
    render(<UserList />);
    fireEvent.click(await screen.findByText('删除'));
    expect(screen.queryByText('张三')).not.toBeInTheDocument();
  });

  test('API 调用失败显示错误消息', async () => {
    axios.get.mockRejectedValue(new Error('网络错误'));
    render(<UserList />);
    expect(await screen.findByText('获取用户失败: 网络错误')).toBeInTheDocument();
  });
});
端到端测试

提示词:

为以下 React 组件编写 Cypress 端到端测试:
- 组件名称:UserList
- 功能:显示用户列表,支持添加和删除用户
- 测试场景:
  1. 初始加载显示用户列表
  2. 添加用户后列表更新
  3. 删除用户后列表更新
  4. API 调用失败显示错误消息

GPT 生成的 Cypress 测试:

describe('用户管理页面', () => {
  beforeEach(() => {
    cy.intercept('GET', '/api/users', { body: [] }).as('getUsers');
    cy.visit('/users');
  });

  it('初始加载显示空列表', () => {
    cy.get('li').should('not.exist');
  });

  it('添加用户后列表更新', () => {
    cy.get('button').contains('添加用户').click();
    cy.get('li').should('contain', '新用户');
  });

  it('删除用户后列表更新', () => {
    cy.intercept('GET', '/api/users', {
      body: [{ id: 1, name: '张三', email: 'zhangsan@example.com' }],
    }).as('getUsers');
    cy.visit('/users');
    cy.get('button').contains('删除').click();
    cy.get('li').should('not.exist');
  });

  it('API 调用失败显示错误消息', () => {
    cy.intercept('GET', '/api/users', { statusCode: 500, body: {} }).as('getUsers');
    cy.visit('/users');
    cy.get('div').should('contain', '获取用户失败');
  });
});
Playwright 测试

提示词:

为相同的 UserList 组件编写 Playwright 端到端测试。

GPT 生成的 Playwright 测试:

const { test, expect } = require('@playwright/test');

test.describe('用户管理页面', () => {
  test('初始加载显示空列表', async ({ page }) => {
    await page.route('/api/users', route => route.fulfill({ body: JSON.stringify([]) }));
    await page.goto('/users');
    await expect(page.locator('li')).toHaveCount(0);
  });

  test('添加用户后列表更新', async ({ page }) => {
    await page.route('/api/users', route => route.fulfill({ body: JSON.stringify([]) }));
    await page.goto('/users');
    await page.click('text=添加用户');
    await expect(page.locator('li')).toContainText('新用户');
  });

  test('删除用户后列表更新', async ({ page }) => {
    await page.route('/api/users', route => route.fulfill({
      body: JSON.stringify([{ id: 1, name: '张三', email: 'zhangsan@example.com' }]),
    }));
    await page.goto('/users');
    await page.click('text=删除');
    await expect(page.locator('li')).toHaveCount(0);
  });

  test('API 调用失败显示错误消息', async ({ page }) => {
    await page.route('/api/users', route => route.fulfill({ status: 500 }));
    await page.goto('/users');
    await expect(page.locator('div')).toContainText('获取用户失败');
  });
});

5. 使用 GPT 模拟 API

生成模拟数据

提示词:

生成一个模拟的用户列表 JSON 数据,包含 id、name 和 email 字段,生成 5 个用户。

GPT 生成的响应:

[
  { "id": 1, "name": "张三", "email": "zhangsan@example.com" },
  { "id": 2, "name": "李四", "email": "lisi@example.com" },
  { "id": 3, "name": "王五", "email": "wangwu@example.com" },
  { "id": 4, "name": "赵六", "email": "zhaoliu@example.com" },
  { "id": 5, "name": "孙七", "email": "sunqi@example.com" }
]
在测试中使用

src/components/UserList.test.tsx

import { render, screen } from '@testing-library/react';
import axios from 'axios';
import UserList from './UserList';

jest.mock('axios');

test('渲染用户列表', async () => {
  const mockData = [
    { id: 1, name: '张三', email: 'zhangsan@example.com' },
    { id: 2, name: '李四', email: 'lisi@example.com' },
  ];
  axios.get.mockResolvedValue({ data: mockData });

  render(<UserList />);
  expect(await screen.findByText('张三')).toBeInTheDocument();
  expect(await screen.findByText('李四')).toBeInTheDocument();
});

6. 使用 GPT 分析错误

ErrorAnalyzer 组件

src/components/ErrorAnalyzer.tsx

import { useState } from 'react';
import axios from 'axios';

function ErrorAnalyzer() {
  const [errorLog, setErrorLog] = useState('');
  const [analysis, setAnalysis] = useState('');
  const [isLoading, setIsLoading] = useState(false);

  const handleAnalyze = async () => {
    setIsLoading(true);
    try {
      const response = await axios.post('[invalid url, do not cite]
        { errorLog }
      );
      setAnalysis(response.data.analysis);
    } catch (error) {
      setAnalysis('分析失败,请检查网络连接');
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div className="p-4 bg-white rounded-lg shadow">
      <h2 className="text-xl font-bold mb-4">错误分析</h2>
      <textarea
        value={errorLog}
        onChange={(e) => setErrorLog(e.target.value)}
        className="w-full p-2 border rounded-lg"
        placeholder="输入错误日志..."
        rows={5}
      />
      <button
        onClick={handleAnalyze}
        className="mt-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400"
        disabled={isLoading}
      >
        {isLoading ? '分析中...' : '分析错误'}
      </button>
      {analysis && (
        <div className="mt-4 p-2 bg-gray-100 rounded-lg">
          <h3 className="font-semibold">分析结果</h3>
          <pre>{analysis}</pre>
        </div>
      )}
    </div>
  );
}

export default ErrorAnalyzer;
示例:错误解析

错误日志:

TypeError: Cannot read property 'map' of undefined

提示词:

分析以下错误并提供解决方案:
TypeError: Cannot read property 'map' of undefined

GPT 响应:

**错误分析**:  
此错误表示代码尝试在 undefined 值上调用 map 方法。可能原因包括:
1. 数据未正确初始化,可能是一个空对象或未定义的变量。
2. API 调用返回 null 或 undefined。
3. 异步数据未正确处理,导致渲染时数据尚未加载。

**解决方案**:
1. **检查数据是否定义**:
```javascript
const data = fetchData();
const list = data ? data.map(item => item.name) : [];
  1. 使用可选链
const list = data?.map(item => item.name) || [];
  1. 确保异步数据加载
useEffect(() => {
  async function fetchData() {
    try {
      const response = await axios.get('/api/data');
      setData(response.data);
    } catch (error) {
      setError('数据加载失败');
    }
  }
  fetchData();
}, []);

### 7. 集成自动化测试

#### GitHub Actions 配置

`github/workflows/test.yml`:

```yaml
name: Run Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Install Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '18'
      - name: Install dependencies
        run: npm install
      - name: Run Jest tests
        run: npm test
      - name: Run Cypress tests
        uses: cypress-io/github-action@v4
        with:
          start: npm run dev
          wait-on: '[invalid url, do not cite]
      - name: Run Playwright tests
        run: npx playwright test
TestRunner 组件

src/components/TestRunner.tsx

import { useState } from 'react';
import ResultDisplay from './ResultDisplay';

interface TestResult {
  passed: number;
  failed: number;
  coverage: number;
}

function TestRunner() {
  const [result, setResult] = useState<TestResult | null>(null);
  const [isRunning, setIsRunning] = useState(false);

  const runTests = async () => {
    setIsRunning(true);
    try {
      // 模拟运行测试
      const response = await fetch('[invalid url, do not cite]
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ command: 'npm test' }),
      );
      const data = await response.json();
      setResult({
        passed: data.passed || 10,
        failed: data.failed || 0,
        coverage: data.coverage || 85,
      });
    } catch (error) {
      console.error('运行测试失败:', error);
    } finally {
      setIsRunning(false);
    }
  };

  return (
    <div className="p-4 bg-white rounded-lg shadow">
      <h2 className="text-xl font-bold mb-4">运行测试</h2>
      <button
        onClick={runTests}
        className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400"
        disabled={isRunning}
      >
        {isRunning ? '运行中...' : '运行测试'}
      </button>
      {result && <ResultDisplay result={result} />}
    </div>
  );
}

export default TestRunner;
ResultDisplay 组件

src/components/ResultDisplay.tsx

interface ResultDisplayProps {
  result: { passed: number; failed: number; coverage: number };
}

function ResultDisplay({ result }: ResultDisplayProps) {
  return (
    <div className="mt-4 p-2 bg-gray-100 rounded-lg">
      <h3 className="font-semibold">测试结果</h3>
      <p>通过: {result.passed}</p>
      <p>失败: {result.failed}</p>
      <p>覆盖率: {result.coverage}%</p>
    </div>
  );
}

export default ResultDisplay;

8. App 组件

src/App.tsx

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import TestGenerator from './components/TestGenerator';
import TestRunner from './components/TestRunner';
import ErrorAnalyzer from './components/ErrorAnalyzer';

const queryClient = new QueryClient();

function App() {
  const [generatedCode, setGeneratedCode] = useState('');

  const handleGenerate = (code: string, type: 'jest' | 'cypress' | 'playwright') => {
    setGeneratedCode(`// ${type} 测试\n${code}`);
  };

  return (
    <QueryClientProvider client={queryClient}>
      <div className="min-h-screen bg-gray-100 flex flex-col items-center p-4">
        <h1 className="text-3xl font-bold mb-4">AI 代码审查员</h1>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4 w-full max-w-4xl">
          <TestGenerator onGenerate={handleGenerate} />
          <TestRunner />
          <ErrorAnalyzer />
          {generatedCode && (
            <div className="p-4 bg-white rounded-lg shadow">
              <h2 className="text-xl font-bold mb-4">生成代码</h2>
              <pre className="p-2 bg-gray-100 rounded-lg">{generatedCode}</pre>
            </div>
          )}
        </div>
      </div>
    </QueryClientProvider>
  );
}

export default App;

9. 提示词设计

提示词设计是 AI 生成测试用例的关键。以下是设计原则:

  1. 明确组件行为:描述组件的功能、输入和输出。
  2. 指定测试类型:明确是单元测试(Jest)还是端到端测试(Cypress/Playwright)。
  3. 提供测试场景:列出常见场景和边界情况。
  4. 示例代码:提供组件代码或 HTML 结构,增加生成准确性。
示例提示词
为以下 React 组件生成 Jest 测试用例,使用 @testing-library/react:
- 组件名称:Counter
- 功能:显示计数并通过按钮递增
- 测试场景:
  1. 初始渲染时,count 为 0
  2. 点击按钮后,count 增加 1
  3. 多次点击按钮,count 正确递增
  4. 边界情况:初始 count 为负数

```js
import React, { useState } from 'react';

function Counter({ initialCount = 0 }) {
  const [count, setCount] = useState(initialCount);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default Counter;

GPT 生成的测试:

```ts
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';

describe('Counter 组件', () => {
  test('初始渲染时 count 为 0', () => {
    render(<Counter />);
    expect(screen.getByText('Count: 0')).toBeInTheDocument();
  });

  test('点击 Increment 按钮后 count 增加 1', () => {
    render(<Counter />);
    fireEvent.click(screen.getByText('Increment'));
    expect(screen.getByText('Count: 1')).toBeInTheDocument();
  });

  test('多次点击 Increment 按钮,count 正确递增', () => {
    render(<Counter />);
    fireEvent.click(screen.getByText('Increment'));
    fireEvent.click(screen.getByText('Increment'));
    expect(screen.getByText('Count: 2')).toBeInTheDocument();
  });

  test('初始 count 为负数时渲染正确', () => {
    render(<Counter initialCount={-5} />);
    expect(screen.getByText('Count: -5')).toBeInTheDocument();
  });
});

10. 性能优化

测试覆盖率分析

使用 Jest 覆盖率报告:

npm test -- --coverage

提示词:

分析以下 Jest 覆盖率报告,识别未覆盖的代码分支并建议测试用例:

{
“total”: {
“lines”: { “total”: 100, “covered”: 80, “skipped”: 0, “pct”: 80 },
“statements”: { “total”: 120, “covered”: 90, “skipped”: 0, “pct”: 75 },
“functions”: { “total”: 50, “covered”: 40, “skipped”: 0, “pct”: 80 },
“branches”: { “total”: 60, “covered”: 40, “skipped”: 0, “pct”: 66.67 }
}
}

GPT 响应:

**覆盖率分析**:  
- 行覆盖率:80%,表明 20% 的代码行未被测试。
- 分支覆盖率:66.67%,表明部分条件分支未覆盖。
- 函数覆盖率:80%,表明部分函数未被调用。

**建议**:
1. **未覆盖的条件分支**:检查组件中的 if-else 或 switch 语句,确保每个分支都有测试用例。
   ```javascript
   if (condition) {
     // 测试此分支
   } else {
     // 测试此分支
   }
  1. 边界情况:为异常输入(如空值、负数)添加测试。
  2. 异步操作:确保所有异步函数(如 API 调用)都有成功和失败的测试用例。

#### 优化测试执行

- **并行测试**:配置 Jest 和 Playwright 支持并行运行。
- **缓存测试结果**:使用 React Query 缓存 API 模拟数据。
- **减少重渲染**:优化测试用例中的渲染次数。

### 11. 部署

#### 构建项目

```bash
npm run build
部署到 Vercel
  1. 注册 Vercel:访问 Vercel 官网 并创建账号。
  2. 新建项目:选择“New Project”。
  3. 导入仓库:将项目推送至 GitHub 并导入。
  4. 配置构建
    • 构建命令:npm run build
    • 输出目录:dist
  5. 部署:点击“Deploy”。

后端需单独部署到 Vercel 或其他平台。


练习:添加测试覆盖率分析功能

为巩固所学,设计一个练习:为应用添加测试覆盖率分析功能。

需求

  • 显示 Jest 测试覆盖率报告。
  • 分析未覆盖的代码分支,生成补充测试用例。
  • 提供交互式界面,允许用户查看覆盖率详情。

实现步骤

  1. 运行覆盖率测试
    配置 Jest 生成覆盖率报告:
npm test -- --coverage
  1. 解析覆盖率数据
    创建组件显示覆盖率:

src/components/CoverageAnalyzer.tsx

import { useState } from 'react';
import axios from 'axios';

function CoverageAnalyzer() {
  const [coverage, setCoverage] = useState<any>(null);
  const [isLoading, setIsLoading] = useState(false);

  const fetchCoverage = async () => {
    setIsLoading(true);
    try {
      const response = await axios.get('[invalid url, do not cite]
      setCoverage(response.data);
    } catch (error) {
      console.error('获取覆盖率失败:', error);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div className="p-4 bg-white rounded-lg shadow">
      <h2 className="text-xl font-bold mb-4">覆盖率分析</h2>
      <button
        onClick={fetchCoverage}
        className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400"
        disabled={isLoading}
      >
        {isLoading ? '加载中...' : '获取覆盖率'}
      </button>
      {coverage && (
        <div className="mt-4 p-2 bg-gray-100 rounded-lg">
          <p>行覆盖率: {coverage.total.lines.pct}%</p>
          <p>分支覆盖率: {coverage.total.branches.pct}%</p>
          <p>函数覆盖率: {coverage.total.functions.pct}%</p>
        </div>
      )}
    </div>
  );
}

export default CoverageAnalyzer;
  1. 生成补充测试
    使用 GPT 分析覆盖率并生成测试用例:

提示词:

基于以下覆盖率报告,生成补充测试用例以覆盖未测试的分支:

{
“total”: {
“lines”: { “total”: 100, “covered”: 80, “skipped”: 0, “pct”: 80 },
“branches”: { “total”: 60, “covered”: 40, “skipped”: 0, “pct”: 66.67 }
}
}

GPT 响应:

// 补充测试用例
describe('未覆盖的分支', () => {
  test('处理空数据', () => {
    render(<Component data={null} />);
    expect(screen.getByText('无数据')).toBeInTheDocument();
  });

  test('处理异常输入', () => {
    render(<Component data={{ invalid: true }} />);
    expect(screen.getByText('无效输入')).toBeInTheDocument();
  });
});

练习目标

通过此练习,您将学会使用 AI 分析测试覆盖率并生成补充测试用例,提升项目的代码质量。


注意事项

  • 提示词设计:确保提示词清晰、具体,包含组件代码和测试场景。
  • 人工审查:AI 生成的测试用例需人工验证,避免遗漏边缘情况。
  • 性能优化:限制 API 调用频率,避免超限。
  • 调试体验:优先使用 Cypress 的交互式调试界面,快速定位问题。
  • 学习建议:参考 Jest 文档Cypress 文档Playwright 文档

结语

通过这个 AI 驱动的前端测试项目,您完整体验了一个 React 项目从需求分析到部署的全流程,掌握了 GPT 驱动的测试用例生成、API 模拟、错误分析和自动化测试集成等技能。这些知识将成为您开发高质量前端应用的坚实基础。

AI 驱动的测试技术在 2025 年将进一步推动开发效率和代码质量的提升。希望您继续探索 AI 辅助开发、自动化测试等趋势,打造更高效的开发流程。欢迎在社区分享成果,一起成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EndingCoder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

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

抵扣说明:

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

余额充值