/api-testing
API 接口测试方法论,覆盖 RESTful/GraphQL 接口的功能、边界、安全、契约测试,参考 Supertest、REST Assured 等开源实践
Claude Code
$ npx -y skills add echoVic/boss-skill --skill api-testing --agent claude-code
Codex
$ npx skills add echoVic/boss-skill --skill api-testing -a codex -y
Cursor
$ npx skills add echoVic/boss-skill --skill api-testing -a cursor -y
Gemini CLI
$ npx skills add echoVic/boss-skill --skill api-testing -a gemini-cli -y
GitHub Copilot
$ npx skills add echoVic/boss-skill --skill api-testing -a github-copilot -y
OpenCode
$ npx skills add echoVic/boss-skill --skill api-testing -a opencode -y
Windsurf
$ npx skills add echoVic/boss-skill --skill api-testing -a windsurf -y
SKILL.md
api-testing.SKILL.mdname: qa/api-testing
description: API 接口测试方法论,覆盖 RESTful/GraphQL 接口的功能、边界、安全、契约测试,参考 Supertest、REST Assured 等开源实践
version: 1.0.0
agent: qa
type: methodology
user-invocable: false
agent-invocable: true
dependencies:
- shared/tech-stack-detection
triggers:
- 需要编写或执行 API 接口测试时
- 需要验证 API 契约一致性时
- 需要覆盖接口边界和安全场景时
metadata:
internal: true
API 接口测试方法论
> 参考开源实践:Supertest、REST Assured、Postman/Newman、HTTPie、Testcontainers
适用场景
- 后端 API 端点需要功能验证
- 需要测试接口边界值、错误处理、权限控制
- 需要验证 API 契约(请求/响应 schema 一致性)
- 需要覆盖认证、授权、越权等安全场景
- 需要测试完整业务流程的 API 链路
测试类型矩阵
| 测试类型 | 目的 | 工具 | 示例 | | ------------ | ----------------------- | ------------------------ | ------------------------------- | | **功能测试** | 验证接口正常行为 | Supertest / REST Assured | 创建资源返回 201 | | **边界测试** | 验证边界值和错误处理 | Supertest | 空 payload、超长字符串、非法 ID | | **安全测试** | 认证、授权、越权 | Supertest + auth header | 无 Token 访问受保护资源 | | **契约测试** | 请求/响应 schema 一致性 | Zod / JSON Schema | 响应字段类型校验 | | **流程测试** | 多接口业务链路 | Supertest 链式调用 | 注册→登录→下单→支付 |
1. RESTful API 测试
1.1 Node.js (Supertest + Jest/Vitest)
// tests/api/users.test.ts
import request from 'supertest';
import { app } from '../../src/app';
describe('User API', () => {
describe('POST /api/users', () => {
it('creates user with valid data', async () => {
const res = await request(app)
.post('/api/users')
.send({ name: 'Alice', email: 'alice@example.com' })
.expect(201)
.expect('Content-Type', /json/);
expect(res.body.success).toBe(true);
expect(res.body.data).toMatchObject({ name: 'Alice', email: 'alice@example.com' });
expect(res.body.data.id).toBeDefined();
});
it('rejects empty name with 400', async () => {
const res = await request(app)
.post('/api/users')
.send({ name: '', email: 'test@example.com' })
.expect(400);
expect(res.body.error.code).toBe('VALIDATION_ERROR');
});
it('rejects duplicate email with 409', async () => {
await request(app).post('/api/users').send({ name: 'A', email: 'dup@example.com' });
const res = await request(app)
.post('/api/users')
.send({ name: 'B', email: 'dup@example.com' })
.expect(409);
expect(res.body.error.code).toBe('CONFLICT');
});
});
});1.2 Python (pytest + requests)
# tests/api/test_users.py
import pytest
import requests
BASE_URL = "http://localhost:3000"
def test_create_user():
res = requests.post(f"{BASE_URL}/api/users", json={"name": "Alice", "email": "alice@example.com"})
assert res.status_code == 201
assert res.json()["data"]["name"] == "Alice"
def test_create_user_empty_name():
res = requests.post(f"{BASE_URL}/api/users", json={"name": "", "email": "test@example.com"})
assert res.status_code == 4001.3 Go (net/http/httptest)
// handlers/users_test.go
func TestCreateUser(t *testing.T) {
app := setupTestApp()
body := strings.NewReader(`{"name":"Alice","email":"alice@example.com"}`)
req := httptest.NewRequest("POST", "/api/users", body)
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
app.ServeHTTP(w, req)
if w.Code != http.StatusCreated {
t.Errorf("expected 201, got %d", w.Code)
}
}2. GraphQL API 测试
// tests/api/graphql.test.ts
import request from 'supertest';
import { app } from '../../src/app';
describe('GraphQL API', () => {
it('queries user by id', async () => {
const query = `
query GetUser($id: ID!) {
user(id: $id) { id name email }
}
`;
const res = await request(app)
.post('/graphql')
.send({ query, variables: { id: '1' } })
.expect(200);
expect(res.body.data.user).toBeDefined();
expect(res.body.errors).toBeUndefined();
});
it('mutates: create user', async () => {
const mutation = `
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) { id name }
}
`;
const res = await request(app)
.post('/graphql')
.send({ mutation, variables: { input: { name: 'Bob', email: 'bob@example.com' } } })
.expect(200);
expect(res.body.data.createUser.id).toBeDefined();
});
});3. 认证与授权测试
3.1 Token 认证
describe('Protected endpoints', () => {
let token: string;
beforeAll(async () => {
const res = await request(app)
.post('/api/auth/login')
.send({ email: 'admin@example.com', password: '<PASSWORD>' });
token = res.body.data.token;
});
it('returns 401 without token', async () => {
await request(app).get('/api/users/profile').expect(401);
});
it('returns 401 with invalid token', async () => {
await request(app)
.get('/api/users/profile')
.set('Authorization', 'Bearer <INVALID_TOKEN>')
.expect(401);
});
it('returns 200 with valid token', async () => {
await request(app)
.get('/api/users/profile')
.set('Authorization', `Bearer ${token}`)
.expect(200);
});
});3.2 权限/越权测试
describe('Authorization', () => {
let adminToken: string;
let userToken: string;
beforeAll(async () => {
adminToken = await login('admin@example.com', '<PASSWORD>');
userToken = await login('user@example.com', '<PASSWORD>');
});
it('admin can delete any user', async () => {
await request(app)
.delete('/api/users/123')
.set('Authorization', `Bearer ${adminToken}`)
.expect(200);
});
it('regular user cannot delete others (403)', async () => {
await request(app)
.delete('/api/users/123')
.set('Authorization', `Bearer ${userToken}`)
.expect(403);
});
});4. 边界值与错误用例
4.1 输入边界
describe('Input boundaries', () =>Read more
name: qa/api-testing description: API 接口测试方法论,覆盖 RESTful/GraphQL 接口的功能、边界、安全、契约测试,参考 Supertest、REST Assured 等开源实践 version: 1.0.0 agent: qa type: methodology user-invocable: false agent-invocable: true dependencies: - shared/tech-stack-detection triggers: - 需要编写或执行 API 接口测试时 - 需要验证 API 契约一致性时 - 需要覆盖接口边界和安全场景时 metadata: internal: true
API 接口测试方法论
> 参考开源实践:Supertest、REST Assured、Postman/Newman、HTTPie、Testcontainers
适用场景
- 后端 API 端点需要功能验证
- 需要测试接口边界值、错误处理、权限控制
- 需要验证 API 契约(请求/响应 schema 一致性)
- 需要覆盖认证、授权、越权等安全场景
- 需要测试完整业务流程的 API 链路
测试类型矩阵
| 测试类型 | 目的 | 工具 | 示例 | | ------------ | ----------------------- | ------------------------ | ------------------------------- | | **功能测试** | 验证接口正常行为 | Supertest / REST Assured | 创建资源返回 201 | | **边界测试** | 验证边界值和错误处理 | Supertest | 空 payload、超长字符串、非法 ID | | **安全测试** | 认证、授权、越权 | Supertest + auth header | 无 Token 访问受保护资源 | | **契约测试** | 请求/响应 schema 一致性 | Zod / JSON Schema | 响应字段类型校验 | | **流程测试** | 多接口业务链路 | Supertest 链式调用 | 注册→登录→下单→支付 |
1. RESTful API 测试
1.1 Node.js (Supertest + Jest/Vitest)
// tests/api/users.test.ts
import request from 'supertest';
import { app } from '../../src/app';
describe('User API', () => {
describe('POST /api/users', () => {
it('creates user with valid data', async () => {
const res = await request(app)
.post('/api/users')
.send({ name: 'Alice', email: 'alice@example.com' })
.expect(201)
.expect('Content-Type', /json/);
expect(res.body.success).toBe(true);
expect(res.body.data).toMatchObject({ name: 'Alice', email: 'alice@example.com' });
expect(res.body.data.id).toBeDefined();
});
it('rejects empty name with 400', async () => {
const res = await request(app)
.post('/api/users')
.send({ name: '', email: 'test@example.com' })
.expect(400);
expect(res.body.error.code).toBe('VALIDATION_ERROR');
});
it('rejects duplicate email with 409', async () => {
await request(app).post('/api/users').send({ name: 'A', email: 'dup@example.com' });
const res = await request(app)
.post('/api/users')
.send({ name: 'B', email: 'dup@example.com' })
.expect(409);
expect(res.body.error.code).toBe('CONFLICT');
});
});
});1.2 Python (pytest + requests)
# tests/api/test_users.py
import pytest
import requests
BASE_URL = "http://localhost:3000"
def test_create_user():
res = requests.post(f"{BASE_URL}/api/users", json={"name": "Alice", "email": "alice@example.com"})
assert res.status_code == 201
assert res.json()["data"]["name"] == "Alice"
def test_create_user_empty_name():
res = requests.post(f"{BASE_URL}/api/users", json={"name": "", "email": "test@example.com"})
assert res.status_code == 4001.3 Go (net/http/httptest)
// handlers/users_test.go
func TestCreateUser(t *testing.T) {
app := setupTestApp()
body := strings.NewReader(`{"name":"Alice","email":"alice@example.com"}`)
req := httptest.NewRequest("POST", "/api/users", body)
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
app.ServeHTTP(w, req)
if w.Code != http.StatusCreated {
t.Errorf("expected 201, got %d", w.Code)
}
}2. GraphQL API 测试
// tests/api/graphql.test.ts
import request from 'supertest';
import { app } from '../../src/app';
describe('GraphQL API', () => {
it('queries user by id', async () => {
const query = `
query GetUser($id: ID!) {
user(id: $id) { id name email }
}
`;
const res = await request(app)
.post('/graphql')
.send({ query, variables: { id: '1' } })
.expect(200);
expect(res.body.data.user).toBeDefined();
expect(res.body.errors).toBeUndefined();
});
it('mutates: create user', async () => {
const mutation = `
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) { id name }
}
`;
const res = await request(app)
.post('/graphql')
.send({ mutation, variables: { input: { name: 'Bob', email: 'bob@example.com' } } })
.expect(200);
expect(res.body.data.createUser.id).toBeDefined();
});
});3. 认证与授权测试
3.1 Token 认证
describe('Protected endpoints', () => {
let token: string;
beforeAll(async () => {
const res = await request(app)
.post('/api/auth/login')
.send({ email: 'admin@example.com', password: '<PASSWORD>' });
token = res.body.data.token;
});
it('returns 401 without token', async () => {
await request(app).get('/api/users/profile').expect(401);
});
it('returns 401 with invalid token', async () => {
await request(app)
.get('/api/users/profile')
.set('Authorization', 'Bearer <INVALID_TOKEN>')
.expect(401);
});
it('returns 200 with valid token', async () => {
await request(app)
.get('/api/users/profile')
.set('Authorization', `Bearer ${token}`)
.expect(200);
});
});3.2 权限/越权测试
describe('Authorization', () => {
let adminToken: string;
let userToken: string;
beforeAll(async () => {
adminToken = await login('admin@example.com', '<PASSWORD>');
userToken = await login('user@example.com', '<PASSWORD>');
});
it('admin can delete any user', async () => {
await request(app)
.delete('/api/users/123')
.set('Authorization', `Bearer ${adminToken}`)
.expect(200);
});
it('regular user cannot delete others (403)', async () => {
await request(app)
.delete('/api/users/123')
.set('Authorization', `Bearer ${userToken}`)
.expect(403);
});
});4. 边界值与错误用例
4.1 输入边界
describe('Input boundaries', () =>Languages / 语言 / 言語 / 언어 / Idiomas / Langues: English · 中文 · 日本語 · 한국어 · Español · Français · Português This project is part of the AtomGit G-Star Incubation Program.
Other skills on boss.
brainstorming
你是 Boss 的前置环节。用户通常只会丢过来一句话——可能是"帮我做个 XX"或者"我想搞个 XX"。你的工作是把这句话**翻译成下游流水线能跑起来的需求**。
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
accessibility
Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…
compiler-commit
Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…
Questions about api-testing.
- Which coding agents can use api-testing?
- api-testing installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

