22.7 外掛測試與除錯
概述
測試與除錯是外掛開發過程中的重要環節,確保外掛質量和穩定性。本章節將詳細介紹 Claude Code 外掛的測試策略、除錯技術和最佳實踐。
测试策略
1. 测试金字塔
外掛測試應遵循測試金字塔原則:
bash
UI 测试
/
集成测试
/
单元测试
* **單元測試** :測試最小的功能單元(函式、方法)
* **整合測試** :測試外掛內部元件之間的互動
* **UI 測試** :測試外掛與使用者介面的互動
### 2\. 测试类型
#### 单元测试單元測試關注外掛的最小功能單元:
python
typescript
// 单元测试示例
import { CalculatorTool } from './calculator-tool';
describe('CalculatorTool', () => {
let tool: CalculatorTool;
beforeEach(() => {
tool = new CalculatorTool();
});
describe('add', () => {
it('should add two numbers', async () => {
const result = await tool.execute({ operation: 'add', a: 2, b: 3 });
expect(result).toBe(5);
});
it('should handle negative numbers', async () => {
const result = await tool.execute({ operation: 'add', a: -2, b: 3 });
expect(result).toBe(1);
});
});
describe('validate', () => {
it('should validate required parameters', () => {
const result = tool.validate({});
expect(result.valid).toBe(false);
expect(result.error).toBe('Operation is required');
});
});
});
#### 集成测试整合測試關注外掛內部元件之間的互動:
python
typescript
// 集成测试示例
import { UserPlugin } from './user-plugin';
import { DatabaseService } from './database-service';
describe('UserPlugin', () => {
let plugin: UserPlugin;
let databaseService: DatabaseService;
beforeEach(() => {
databaseService = new DatabaseService();
plugin = new UserPlugin({ databaseService });
});
it('should create user and store in database', async () => {
const user = await plugin.createUser({ name: 'John', email: 'john@example.com' });
const storedUser = await databaseService.getUser(user.id);
expect(storedUser).toBeDefined();
expect(storedUser.name).toBe('John');
});
});
#### 端到端测试端到端測試關注外掛與系統的互動:
python
typescript
// 端到端测试示例
import { PluginManager } from '@claude-code/plugin-sdk';
describe('UserPlugin E2E', () => {
let pluginManager: PluginManager;
beforeAll(async () => {
pluginManager = new PluginManager();
await pluginManager.loadPlugin('user-plugin');
});
afterAll(async () => {
await pluginManager.unloadPlugin('user-plugin');
});
it('should create user through plugin API', async () => {
const result = await pluginManager.executeCommand('user:create', {
name: 'John',
email: 'john@example.com'
});
expect(result.success).toBe(true);
expect(result.data.user.id).toBeDefined();
});
});
## 测试框架与工具
### 1\. JestJest 是一個流行的 JavaScript 測試框架:
json
// package.json
{
"devDependencies": {
"jest": "^29.0.0",
"@types/jest": "^29.0.0"
},
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
### 2\. MochaMocha 是一個靈活的 JavaScript 測試框架:
json
// package.json
{
"devDependencies": {
"mocha": "^10.0.0",
"chai": "^4.0.0",
"sinon": "^15.0.0"
},
"scripts": {
"test": "mocha",
"test:watch": "mocha --watch"
}
}
### 3\. VitestVitest 是一個快速的 Vite-native 測試框架:
json
// package.json
{
"devDependencies": {
"vitest": "^0.30.0"
},
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui"
}
}
## 调试技术
### 1\. 日志调试使用日誌記錄外掛執行時資訊:
python
typescript
// 日志调试示例
class UserPlugin {
async createUser(userData) {
this.logger.debug('Creating user', { userData });
try {
const user = await this.database.create(userData);
this.logger.info('User created', { userId: user.id });
return user;
} catch (error) {
this.logger.error('Failed to create user', { error: error.message });
throw error;
}
}
}
### 2\. 断点调试使用 VS Code 進行斷點除錯:
json
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Plugin",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/src/plugin.ts",
"args": ["--debug"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
}
]
}
### 3\. 远程调试外掛支援遠端除錯:
typescript
// 启用远程调试
if (process.env.NODE_ENV === 'development') {
require('inspector').open(9229, '0.0.0.0', true);
}
## 测试环境搭建
### 1\. 测试数据库使用測試資料庫進行整合測試:
javascript
typescript
// 测试数据库配置
const testConfig = {
database: {
type: 'sqlite',
database: ':memory:',
synchronize: true,
logging: false
}
};
### 2\. 测试替身使用測試替身模擬外部依賴:
python
typescript
// 使用 Sinon 模拟依赖
import sinon from 'sinon';
describe('UserPlugin', () => {
it('should send welcome email', async () => {
const emailService = { send: sinon.stub().resolves(true) };
const plugin = new UserPlugin({ emailService });
await plugin.createUser({ name: 'John', email: 'john@example.com' });
expect(emailService.send.calledOnce).toBe(true);
expect(emailService.send.calledWith('john@example.com', 'Welcome')).toBe(true);
});
});
### 3\. 测试数据使用測試資料進行測試:
python
typescript
// 测试数据生成
function generateTestUser() {
return {
name: `Test User ${Math.random().toString(36).substring(2, 10)}`,
email: `test-${Math.random().toString(36).substring(2, 10)}@example.com`,
password: 'password123'
};
}
// 批量生成测试数据
function generateTestUsers(count: number) {
return Array.from({ length: count }, () => generateTestUser());
}
## 持续集成
### 1\. GitHub Actions使用 GitHub Actions 進行持續整合:
yaml
yaml
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm install
- run: npm test
- run: npm run lint
### 2\. GitLab CI使用 GitLab CI 進行持續整合:
yaml
yaml
# .gitlab-ci.yml
stages:
- test
unit_test:
stage: test
image: node:18
script:
- npm install
- npm test
## 性能测试
### 1\. 基准测试使用基準測試評估外掛效能:
python
typescript
// 基准测试示例
import { suite } from 'benchmark';
import { CalculatorTool } from './calculator-tool';
const tool = new CalculatorTool();
const bench = new suite('CalculatorTool');
bench
.add('add', async (deferred) => {
await tool.execute({ operation: 'add', a: 2, b: 3 });
deferred.resolve();
}, { defer: true })
.add('multiply', async (deferred) => {
await tool.execute({ operation: 'multiply', a: 2, b: 3 });
deferred.resolve();
}, { defer: true })
.on('cycle', (event) => {
console.log(String(event.target));
})
.run();
### 2\. 负载测试使用負載測試評估外掛在高併發下的效能:
python
typescript
// 负载测试示例
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
vus: 100,
duration: '30s'
};
export default function () {
http.post('http://localhost:3000/api/users', JSON.stringify({
name: 'Test User',
email: 'test@example.com'
}), {
headers: { 'Content-Type': 'application/json' }
});
sleep(1);
}
## 测试覆盖率
### 1\. 覆盖率报告使用 Jest 生成覆蓋率報告:
json
// package.json
{
"scripts": {
"test:coverage": "jest --coverage"
}
}
### 2\. 覆盖率阈值設定覆蓋率閾值確保測試質量:
json
// jest.config.js
module.exports = {
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
## 调试技巧
### 1\. 日志级别使用不同的日誌級別除錯:
javascript
typescript
// 日志级别配置
const logger = createLogger({
level: process.env.NODE_ENV === 'development' ? 'debug' : 'info'
});
### 2\. 调试工具使用 Chrome DevTools 除錯:
bash
# 启动调试模式
node --inspect-brk plugin.js
### 3\. 错误追踪使用 Sentry 進行錯誤追蹤:
python
typescript
// Sentry 配置
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: 'your-sentry-dsn',
environment: process.env.NODE_ENV
});
## 最佳实践
### 1\. 测试驱动开发採用測試驅動開發(TDD):
- 編寫失敗的測試
- 編寫足夠的程式碼使測試透過
- 重構程式碼
- 重複
2. 測試隔離
確保測試之間相互隔離:
javascript
typescript
// 测试隔离示例
beforeEach(() => {
// 重置状态
database.clear();
cache.clear();
});
### 3\. 测试命名使用清晰的測試命名規範:
javascript
typescript
// 测试命名示例
describe('UserPlugin', () => {
describe('createUser', () => {
it('should create user with valid data', async () => {
// ...
});
it('should throw error with invalid data', async () => {
// ...
});
});
});
### 4\. 测试文档為測試編寫文件:
javascript
typescript
// 测试文档示例
/**
* 测试用户创建功能
* 1. 验证必填字段
* 2. 验证数据存储
* 3. 验证错误处理
*/
describe('UserPlugin.createUser', () => {
// ...
});
## 常见问题
### Q: 如何测试异步代码?
A: 使用 async/await 测试异步代码:
typescript
it('should handle async operation', async () => {
const result = await asyncFunction();
expect(result).toBe(true);
});
### Q: 如何测试错误情况?
A: 使用 expect.toThrow 测试错误情况:
typescript
it('should throw error with invalid data', async () => {
await expect(plugin.createUser({})).rejects.toThrow('Invalid data');
});
### Q: 如何测试定时器?
A: 使用 Jest 的定时器模拟:
typescript
it('should execute after delay', async () => {
jest.useFakeTimers();
const callback = jest.fn();
setTimeout(callback, 1000);
jest.runAllTimers();
expect(callback).toHaveBeenCalled();
});
## 总结測試與除錯是外掛開發過程中的重要環節。透過採用合適的測試策略、使用測試框架和工具、遵循最佳實踐,可以確保外掛的質量和穩定性。
下一章將介紹外掛效能最佳化技術,幫助開發者提高外掛效能和響應性。