关于 testing
testing 是面向 AI Agent 的代码测试技能,覆盖用例识别、测试编写与运行、覆盖率分析和改进建议。它支持多种语言及其常用测试框架,适合为代码库系统化补充和评估测试。
主要功能
- 01分析代码与依赖关系
- 02识别边界及异常测试场景
- 03编写并运行测试
- 04分析覆盖率并提出改进建议
- 05支持多种语言与测试框架
适用场景
- 01为现有模块生成单元测试
- 02扩展现有测试并检查覆盖缺口
- 03定位测试失败并规划后续改进
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ seb1n/ awesome-ai-agent-skills --skill testing安装后在 Claude Code 中输入/testing确认已加载。
详细介绍
测试
此技能可让 AI Agent 系统地为给定代码库生成、运行和评估测试。它涵盖完整的测试生命周期——从分析源代码、识别有意义的测试用例,到编写和执行测试,再到衡量覆盖率并提出改进建议。该 Agent 支持多种语言和框架的单元测试、集成测试和端到端测试。
工作流
-
分析源代码。 阅读目标文件或模块,构建其函数、类和外部交互的依赖关系图。识别公共接口、内部辅助函数、输入参数、返回类型和副作用。这一步将确定哪些内容可测试,以及适合采用哪些测试类型。
-
识别测试用例。 针对每个函数或方法,列出需要覆盖的场景:正常输入、边界值、无效或空输入、异常路径和状态转换。对于集成点,识别需要模拟或替代测试的协作者,以及需要进行实时测试的协作者。按风险确定优先级——先测试复杂的分支逻辑和公共 API 接口。
-
编写测试。 使用项目现有的测试框架(例如 pytest、Jest、JUnit)生成结构清晰的测试代码。每个测试都应有一个描述性名称,说明测试场景和预期结果。使用 Arrange-Act-Assert 模式:设置前置条件、调用待测代码,并断言预期结果。如果多个输入集适用同一个逻辑用例,则添加参数化测试。
-
运行测试。 使用适当的运行命令执行测试套件。捕获完整输出,包括通过/失败状态、断言消息和耗时信息。如果有测试失败,则解析失败输出,判断失败是源代码中的错误还是测试本身的问题。
-
分析覆盖率。 启用覆盖率检测运行测试套件(例如
pytest --cov、jest --coverage)。解析覆盖率报告,找出未覆盖的行、分支和函数。标记缺少覆盖的关键代码路径——错误处理程序、安全检查和数据验证。 -
提出改进建议。 根据覆盖率缺口和代码复杂度,建议补充测试用例。提出可提高代码可测试性的重构机会,例如提取纯函数或引入依赖注入。提供总结报告,列出覆盖率百分比和按优先级排序的后续操作。
支持的语言
| 语言 | 框架 | 运行命令 |
|---|---|---|
| Python | pytest | pytest --cov=src -v |
| JavaScript | Jest | npx jest --coverage --verbose |
| TypeScript | Jest / Vitest | npx vitest run --coverage |
| Java | JUnit 5 | mvn test |
| Go | testing (stdlib) | go test -cover ./... |
| Rust | cargo test | cargo test |
用法
提供以下一项或多项输入:
- 要为其生成测试的源文件或目录(例如,
src/utils/parser.py)。 - 如果希望 Agent 扩展或改进现有测试,则提供现有测试文件。
- 如果项目尚未配置测试框架,则说明测试框架偏好。
- 如果希望 Agent 达到特定百分比,则提供覆盖率阈值(例如,90%)。
示例
示例 1 — 使用 pytest 测试 Python
给定此源文件 src/cart.py:
class ShoppingCart:
def __init__(self):
self.items = []
def add_item(self, name: str, price: float, quantity: int = 1):
if price < 0:
raise ValueError("Price cannot be negative")
if quantity < 1:
raise ValueError("Quantity must be at least 1")
self.items.append({"name": name, "price": price, "quantity": quantity})
def total(self) -> float:
return sum(item["price"] * item["quantity"] for item in self.items)
def remove_item(self, name: str):
self.items = [item for item in self.items if item["name"] != name]
Agent 生成 tests/test_cart.py:
import pytest
from src.cart import ShoppingCart
@pytest.fixture
def cart():
return ShoppingCart()
def test_add_item_and_total(cart):
cart.add_item("Widget", 9.99, 2)
assert cart.total() == pytest.approx(19.98)
def test_empty_cart_total_is_zero(cart):
assert cart.total() == 0.0
def test_add_item_negative_price_raises(cart):
with pytest.raises(ValueError, match="Price cannot be negative"):
cart.add_item("Bad", -1.0)
def test_add_item_zero_quantity_raises(cart):
with pytest.raises(ValueError, match="Quantity must be at least 1"):
cart.add_item("Bad", 5.0, 0)
def test_remove_item(cart):
cart.add_item("A", 1.0)
cart.add_item("B", 2.0)
cart.remove_item("A")
assert cart.total() == 2.0
def test_remove_nonexistent_item_does_nothing(cart):
cart.add_item("A", 1.0)
cart.remove_item("Z")
assert cart.total() == 1.0
运行:pytest tests/test_cart.py --cov=src -v
示例 2 — 使用 Jest 测试 JavaScript
给定此源文件 src/validator.js:
function isValidEmail(email) {
if (typeof email !== "string") return false;
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
function isStrongPassword(password) {
if (typeof password !== "string") return false;
return (
password.length >= 8 &&
/[A-Z]/.test(password) &&
/[a-z]/.test(password) &&
/[0-9]/.test(password)
);
}
module.exports = { isValidEmail, isStrongPassword };
Agent 生成 src/__tests__/validator.test.js:
const { isValidEmail, isStrongPassword } = require("../validator");
describe("isValidEmail", () => {
test.each([
["[email protected]", true],
["[email protected]", true],
["missing-at-sign.com", false],
["@no-local.com", false],
["spaces [email protected]", false],
["", false],
])("isValidEmail(%s) => %s", (input, expected) => {
expect(isValidEmail(input)).toBe(expected);
});
test("returns false for non-string input", () => {
expect(isValidEmail(null)).toBe(false);
expect(isValidEmail(42)).toBe(false);
});
});
describe("isStrongPassword", () => {
test("accepts a strong password", () => {
expect(isStrongPassword("Str0ngPwd")).toBe(true);
});
test("rejects short password", () => {
expect(isStrongPassword("Ab1")).toBe(false);
});
test("rejects password without uppercase", () => {
expect(isStrongPassword("alllower1")).toBe(false);
});
test("rejects non-string input", () => {
expect(isStrongPassword(undefined)).toBe(false);
});
});
运行:npx jest --coverage --verbose
最佳实践
- 根据场景命名测试,而不是根据实现命名。 使用
test_empty_cart_total_is_zero这样的名称,而不是test_total_method。这样失败时一目了然。 - 保持测试相互独立。 每个测试都应通过 fixture 或 setup 方法自行设置状态。切勿依赖测试的执行顺序。
- 对不同输入优先使用参数化测试。 当相同逻辑适用于多种输入时,使用
@pytest.mark.parametrize或test.each,而不是重复编写测试主体。 - 模拟外部依赖,而不是内部逻辑。 对网络调用、数据库和文件 I/O 使用存根。避免模拟被测代码本身——这会违背测试的目的。
- 关注有意义的覆盖率,而不是 100%。 力求全面覆盖关键路径的分支。琐碎的 getter 和框架生成的代码通常无需专门测试。
- 每次提交都在 CI 中运行测试。 将测试命令集成到项目的 CI 流水线中,以便立即发现回归问题。
边缘情况
- 动态生成的代码: 如果代码库使用元编程、装饰器或代码生成,Agent 可能无法检测到所有可调用路径。请提供有关生成接口的提示。
- 全局状态和单例: 测试会修改全局状态的代码时,需要谨慎地进行清理。Agent 会标记这些情况,但可能需要指导,以确定可接受的重置策略。
- 异步和并发代码: 测试异步函数需要使用特定框架的模式(
pytest-asyncio、Jest 的异步处理)。Agent 会采用适当的模式,但可能会询问超时阈值是否合适。 - 依赖环境的测试: 依赖环境变量、文件系统布局或网络访问的测试应明确标记为集成测试,并从快速单元测试运行中排除。
- 不稳定的测试: 如果测试运行偶尔失败,Agent 会标记非确定性模式(例如依赖墙上时钟时间、比较无序集合),并提出修复建议。