关于 zero-script-qa
zero-script-qa 是一种通过结构化日志和实时监控开展 QA 的方法,面向开发与测试团队。它以人工体验测试结合 AI 日志分析,检测并记录问题,无需编写测试脚本。
主要功能
- 01结构化 JSON 日志规范
- 02跨服务 Request ID 追踪
- 03Docker 日志实时监控
- 04AI 分析错误与异常耗时
- 05自动整理 QA 问题
适用场景
- 01人工测试功能时实时发现错误
- 02按 Request ID 排查跨层请求
- 03监测慢响应与连续失败
- 04检查各环境的日志配置
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ ww-w-ai/ bkit-claude-code --skill zero-script-qa安装后在 Claude Code 中输入/zero-script-qa确认已加载。
详细介绍
零脚本 QA 专家知识
概述
零脚本 QA 是一种通过结构化日志和实时监控验证功能、无需编写测试脚本的方法。
Traditional: Write test code → Execute → Check results → Maintain
Zero Script: Build log infrastructure → Manual UX test → AI log analysis → Auto issue detection
核心原则
1. 记录所有内容
- 所有 API 调用(包括 200 OK)
- 所有错误
- 所有重要业务事件
- 通过 Request ID 跟踪整个流程
2. 结构化 JSON 日志
- 可解析的 JSON 格式
- 统一的字段(timestamp、level、request_id、message、data)
- 为不同环境设置不同的日志级别
3. 实时监控
- Docker 日志流
- Claude Code 实时分析
- 立即发现并记录问题
日志架构
JSON 日志格式标准
{
"timestamp": "2026-01-08T10:30:00.000Z",
"level": "INFO",
"service": "api",
"request_id": "req_abc123",
"message": "API Request completed",
"data": {
"method": "POST",
"path": "/api/users",
"status": 200,
"duration_ms": 45
}
}
必需的日志字段
| 字段 | 类型 | 说明 |
|---|---|---|
| timestamp | ISO 8601 | 发生时间 |
| level | 字符串 | DEBUG、INFO、WARNING、ERROR |
| service | 字符串 | 服务名称(api、web、worker 等) |
| request_id | 字符串 | 请求跟踪 ID |
| message | 字符串 | 日志消息 |
| data | 对象 | 附加数据(可选) |
日志级别策略
| 环境 | 最低级别 | 用途 |
|---|---|---|
| 本地 | DEBUG | 开发和 QA |
| 预发布 | DEBUG | QA 和集成测试 |
| 生产 | INFO | 运维监控 |
Request ID 传播
概念
Client → API Gateway → Backend → Database
↓ ↓ ↓ ↓
req_abc req_abc req_abc req_abc
Trackable with same Request ID across all layers
实现模式
1. 生成 Request ID(入口)
// middleware.ts
import { v4 as uuidv4 } from 'uuid';
export function generateRequestId(): string {
return `req_${uuidv4().slice(0, 8)}`;
}
// Propagate via header
headers['X-Request-ID'] = requestId;
2. 提取并传播 Request ID
// API client
const requestId = headers['X-Request-ID'] || generateRequestId();
// Include in all logs
logger.info('Processing request', { request_id: requestId });
// Include in header when calling downstream services
await fetch(url, {
headers: { 'X-Request-ID': requestId }
});
后端日志记录(FastAPI)
日志中间件
# middleware/logging.py
import logging
import time
import uuid
import json
from fastapi import Request
class JsonFormatter(logging.Formatter):
def format(self, record):
log_record = {
"timestamp": self.formatTime(record),
"level": record.levelname,
"service": "api",
"request_id": getattr(record, 'request_id', 'N/A'),
"message": record.getMessage(),
}
if hasattr(record, 'data'):
log_record["data"] = record.data
return json.dumps(log_record)
class LoggingMiddleware:
async def __call__(self, request: Request, call_next):
request_id = request.headers.get('X-Request-ID', f'req_{uuid.uuid4().hex[:8]}')
request.state.request_id = request_id
start_time = time.time()
# Request logging
logger.info(
f"Request started",
extra={
'request_id': request_id,
'data': {
'method': request.method,
'path': request.url.path,
'query': str(request.query_params)
}
}
)
response = await call_next(request)
duration = (time.time() - start_time) * 1000
# Response logging (including 200 OK!)
logger.info(
f"Request completed",
extra={
'request_id': request_id,
'data': {
'status': response.status_code,
'duration_ms': round(duration, 2)
}
}
)
response.headers['X-Request-ID'] = request_id
return response
业务逻辑日志记录
# services/user_service.py
def create_user(data: dict, request_id: str):
logger.info("Creating user", extra={
'request_id': request_id,
'data': {'email': data['email']}
})
# Business logic
user = User(**data)
db.add(user)
db.commit()
logger.info("User created", extra={
'request_id': request_id,
'data': {'user_id': user.id}
})
return user
前端日志记录(Next.js)
日志记录器模块
// lib/logger.ts
type LogLevel = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR';
interface LogData {
request_id?: string;
[key: string]: any;
}
const LOG_LEVELS: Record<LogLevel, number> = {
DEBUG: 0,
INFO: 1,
WARNING: 2,
ERROR: 3,
};
const MIN_LEVEL = process.env.NODE_ENV === 'production' ? 'INFO' : 'DEBUG';
function log(level: LogLevel, message: string, data?: LogData) {
if (LOG_LEVELS[level] < LOG_LEVELS[MIN_LEVEL]) return;
const logEntry = {
timestamp: new Date().toISOString(),
level,
service: 'web',
request_id: data?.request_id || 'N/A',
message,
data: data ? { ...data, request_id: undefined } : undefined,
};
console.log(JSON.stringify(logEntry));
}
export const logger = {
debug: (msg: string, data?: LogData) => log('DEBUG', msg, data),
info: (msg: string, data?: LogData) => log('INFO', msg, data),
warning: (msg: string, data?: LogData) => log('WARNING', msg, data),
error: (msg: string, data?: LogData) => log('ERROR', msg, data),
};
API 客户端集成
// lib/api-client.ts
import { logger } from './logger';
import { v4 as uuidv4 } from 'uuid';
export async function apiClient<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const requestId = `req_${uuidv4().slice(0, 8)}`;
const startTime = Date.now();
logger.info('API Request started', {
request_id: requestId,
method: options.method || 'GET',
endpoint,
});
try {
const response = await fetch(`/api${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
'X-Request-ID': requestId,
...options.headers,
},
});
const duration = Date.now() - startTime;
const data = await response.json();
// Log 200 OK too!
logger.info('API Request completed', {
request_id: requestId,
status: response.status,
duration_ms: duration,
});
if (!response.ok) {
logger.error('API Request failed', {
request_id: requestId,
status: response.status,
error: data.error,
});
throw new ApiError(data.error);
}
return data;
} catch (error) {
logger.error('API Request error', {
request_id: requestId,
error: error instanceof Error ? error.message : 'Unknown error',
});
throw error;
}
}
Nginx JSON 日志
nginx.conf 配置
http {
log_format json_combined escape=json '{'
'"timestamp":"$time_iso8601",'
'"level":"INFO",'
'"service":"nginx",'
'"request_id":"$http_x_request_id",'
'"message":"HTTP Request",'
'"data":{'
'"remote_addr":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}'
'}';
access_log /var/log/nginx/access.log json_combined;
}
基于 Docker 的 QA 工作流
docker-compose.yml 配置
version: '3.8'
services:
api:
build: ./backend
environment:
- LOG_LEVEL=DEBUG
- LOG_FORMAT=json
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
web:
build: ./frontend
environment:
- NODE_ENV=development
depends_on:
- api
nginx:
image: nginx:alpine
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
ports:
- "80:80"
depends_on:
- api
- web
实时日志监控
# Stream all service logs
docker compose logs -f
# Specific service only
docker compose logs -f api
# Filter errors only
docker compose logs -f | grep '"level":"ERROR"'
# Track specific Request ID
docker compose logs -f | grep 'req_abc123'
QA 自动化工作流
1. 启动环境
# Start development environment
docker compose up -d
# Start log monitoring (Claude Code monitors)
docker compose logs -f
2. 手动 UX 测试
User tests actual features in browser:
1. Sign up attempt
2. Login attempt
3. Use core features
4. Test edge cases
3. Claude Code 日志分析
Claude Code in real-time:
1. Monitor log stream
2. Detect error patterns
3. Detect abnormal response times
4. Track entire flow via Request ID
5. Auto-document issues
4. 问题记录
# QA Issue Report
## Issues Found
### ISSUE-001: Insufficient error handling on login failure
- **Request ID**: req_abc123
- **Severity**: Medium
- **Reproduction path**: Login → Wrong password
- **Log**:
```json
{"level":"ERROR","message":"Login failed","data":{"error":"Invalid credentials"}}
- 问题:错误消息对用户不够友好
- 建议修复:添加错误代码与消息的映射
---
## Issue Detection Patterns
### 1. Error Detection
```json
{"level":"ERROR","message":"..."}
→ 立即报告
2. 检测响应缓慢
{"data":{"duration_ms":3000}}
→ 超过 1000ms 时发出警告
3. 检测连续失败
3+ consecutive failures on same endpoint
→ 报告潜在的系统问题
4. 异常状态代码
{"data":{"status":500}}
→ 立即报告 5xx 错误
阶段集成
| 阶段 | 零脚本 QA 集成 |
|---|---|
| 阶段 4(API) | 验证 API 响应日志 |
| 阶段 6(UI) | 验证前端日志 |
| 阶段 7(安全) | 验证安全事件日志 |
| 阶段 8(审查) | 审查日志质量 |
| 阶段 9(部署) | 配置生产日志级别 |
迭代测试周期模式
基于 bkamp.ai 通知功能开发:
示例:8 轮测试流程
| 轮次 | 通过率 | 发现的 Bug | 已应用的修复 |
|---|---|---|---|
| 第 1 轮 | 30% | DB 架构不匹配 | 架构迁移 |
| 第 2 轮 | 45% | 缺少 NULL 处理 | 添加空值检查 |
| 第 3 轮 | 55% | 路由错误 | 修复 deeplinks |
| 第 4 轮 | 65% | 类型不匹配 | 修复枚举类型 |
| 第 5 轮 | 70% | 计算错误 | 修复计数逻辑 |
| 第 6 轮 | 75% | 缺少事件 | 添加事件触发器 |
| 第 7 轮 | 82% | 缓存同步问题 | 修复缓存失效 |
| 第 8 轮 | 89% | 稳定 | 最终完善 |
周期工作流
┌─────────────────────────────────────────────────────────────┐
│ Iterative Test Cycle │
├─────────────────────────────────────────────────────────────┤
│ │
│ Cycle N: │
│ 1. Run test script (E2E or manual) │
│ 2. Claude monitors logs in real-time │
│ 3. Record pass/fail results │
│ 4. Claude identifies root cause of failures │
│ 5. Fix code immediately (hot reload) │
│ 6. Document: Cycle N → Bug → Fix │
│ │
│ Repeat until acceptable pass rate (>85%) │
│ │
└─────────────────────────────────────────────────────────────┘
E2E 测试脚本模板
#!/bin/bash
# E2E Test Script Template
API_URL="http://localhost:8000"
TOKEN="your-test-token"
PASS_COUNT=0
FAIL_COUNT=0
SKIP_COUNT=0
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[0;33m'
NC='\033[0m'
test_feature_action() {
echo -n "Testing: Feature action... "
response=$(curl -s -X POST "$API_URL/api/v1/feature/action" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"param": "value"}')
if [[ "$response" == *"expected_result"* ]]; then
echo -e "${GREEN}✅ PASS${NC}"
((PASS_COUNT++))
else
echo -e "${RED}❌ FAIL${NC}"
echo "Response: $response"
((FAIL_COUNT++))
fi
}
# Run all tests
test_feature_action
# ... more tests
# Summary
echo ""
echo "═══════════════════════════════════════"
echo "Test Results:"
echo -e " ${GREEN}✅ PASS: $PASS_COUNT${NC}"
echo -e " ${RED}❌ FAIL: $FAIL_COUNT${NC}"
echo -e " ${YELLOW}⏭️ SKIP: $SKIP_COUNT${NC}"
echo "═══════════════════════════════════════"
测试周期记录模板
# Feature Test Results - Cycle N
## Summary
- **Date**: YYYY-MM-DD
- **Feature**: {feature name}
- **Pass Rate**: N%
- **Tests**: X passed / Y total
## Results
| Test Case | Status | Notes |
|-----------|--------|-------|
| Test 1 | ✅ | |
| Test 2 | ❌ | {error description} |
| Test 3 | ⏭️ | {skip reason} |
## Bugs Found
### BUG-001: {Title}
- **Root Cause**: {description}
- **Fix**: {what was changed}
- **Files**: `path/to/file.py:123`
## Next Cycle Plan
- {what to test next}
检查清单
日志基础设施
- 已应用 JSON 日志格式
- Request ID 生成与传播
- 为各环境设置日志级别
- Docker 日志配置
后端日志记录
- 已实现日志中间件
- 记录所有 API 调用(包括 200 OK)
- 业务逻辑日志记录
- 详细记录错误
前端日志记录
- 已实现 Logger 模块
- 已集成 API 客户端
- 已实现错误边界日志记录
QA 工作流
- 已配置 Docker Compose
- 已准备好实时监控
- 已准备好问题文档模板
自动应用规则
构建日志基础设施时
实现 API/后端时:
- 建议创建日志中间件
- 建议设置 JSON 格式的日志记录器
- 添加 Request ID 生成/传递逻辑
实现前端时:
- 建议创建 Logger 模块
- 建议将日志记录集成到 API 客户端
- 建议包含 Request ID 请求头
执行 QA 时
收到测试请求时:
- 指导运行
docker compose logs -f - 请求用户手动测试 UX
- 实时监控日志
- 发现问题后立即记录
- 提供修复建议
问题检测阈值
| 严重级别 | 条件 | 操作 |
|---|---|---|
| 严重 | level: ERROR 或 status: 5xx | 立即报告 |
| 严重 | duration_ms > 3000 | 立即报告 |
| 严重 | 连续失败 3 次或以上 | 立即报告 |
| 警告 | status: 401, 403 | 发出警告报告 |
| 警告 | duration_ms > 1000 | 发出警告报告 |
| 信息 | 缺少日志字段 | 记录为待改进项 |
| 信息 | 未传递 Request ID | 记录为待改进项 |
必需的日志位置
后端(FastAPI/Express)
✅ Request start (method, path, params)
✅ Request complete (status, duration_ms)
✅ Major business logic steps
✅ Detailed info on errors
✅ Before/after external API calls
✅ DB queries (in development)
前端(Next.js/React)
✅ API call start
✅ API response received (status, duration)
✅ Detailed info on errors
✅ Important user actions
评论
支持 Markdown