mcpField
T
testing

testing

github.com/seb1n测试与质量236138 次安装12 周前更新
代码仓库

关于 testing

testing 是面向 AI Agent 的代码测试技能,覆盖用例识别、测试编写与运行、覆盖率分析和改进建议。它支持多种语言及其常用测试框架,适合为代码库系统化补充和评估测试。

主要功能

  1. 01分析代码与依赖关系
  2. 02识别边界及异常测试场景
  3. 03编写并运行测试
  4. 04分析覆盖率并提出改进建议
  5. 05支持多种语言与测试框架

适用场景

  1. 01为现有模块生成单元测试
  2. 02扩展现有测试并检查覆盖缺口
  3. 03定位测试失败并规划后续改进

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/seb1n/awesome-ai-agent-skills --skill testing

安装后在 Claude Code 中输入/testing确认已加载。

详细介绍

测试

此技能可让 AI Agent 系统地为给定代码库生成、运行和评估测试。它涵盖完整的测试生命周期——从分析源代码、识别有意义的测试用例,到编写和执行测试,再到衡量覆盖率并提出改进建议。该 Agent 支持多种语言和框架的单元测试、集成测试和端到端测试。

工作流

  1. 分析源代码。 阅读目标文件或模块,构建其函数、类和外部交互的依赖关系图。识别公共接口、内部辅助函数、输入参数、返回类型和副作用。这一步将确定哪些内容可测试,以及适合采用哪些测试类型。

  2. 识别测试用例。 针对每个函数或方法,列出需要覆盖的场景:正常输入、边界值、无效或空输入、异常路径和状态转换。对于集成点,识别需要模拟或替代测试的协作者,以及需要进行实时测试的协作者。按风险确定优先级——先测试复杂的分支逻辑和公共 API 接口。

  3. 编写测试。 使用项目现有的测试框架(例如 pytest、Jest、JUnit)生成结构清晰的测试代码。每个测试都应有一个描述性名称,说明测试场景和预期结果。使用 Arrange-Act-Assert 模式:设置前置条件、调用待测代码,并断言预期结果。如果多个输入集适用同一个逻辑用例,则添加参数化测试。

  4. 运行测试。 使用适当的运行命令执行测试套件。捕获完整输出,包括通过/失败状态、断言消息和耗时信息。如果有测试失败,则解析失败输出,判断失败是源代码中的错误还是测试本身的问题。

  5. 分析覆盖率。 启用覆盖率检测运行测试套件(例如 pytest --cov、jest --coverage)。解析覆盖率报告,找出未覆盖的行、分支和函数。标记缺少覆盖的关键代码路径——错误处理程序、安全检查和数据验证。

  6. 提出改进建议。 根据覆盖率缺口和代码复杂度,建议补充测试用例。提出可提高代码可测试性的重构机会,例如提取纯函数或引入依赖注入。提供总结报告,列出覆盖率百分比和按优先级排序的后续操作。

支持的语言

语言框架运行命令
Pythonpytestpytest --cov=src -v
JavaScriptJestnpx jest --coverage --verbose
TypeScriptJest / Vitestnpx vitest run --coverage
JavaJUnit 5mvn test
Gotesting (stdlib)go test -cover ./...
Rustcargo testcargo 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 会标记非确定性模式(例如依赖墙上时钟时间、比较无序集合),并提出修复建议。

评论

支持 Markdown