mcpField
T
test-quality-inspector

test-quality-inspector

github.com/bobmatnyc测试与质量78371 次安装15 周前更新
代码仓库

关于 test-quality-inspector

用于检查测试套件中的无效断言、缺失场景及与目标相反的测试,帮助工程师和 QA 发现覆盖缺口并改进测试。

主要功能

  1. 01识别无效或薄弱的断言
  2. 02指出缺失的负向与校验测试
  3. 03核对测试名称与实际行为
  4. 04提供具体的测试改进建议

适用场景

  1. 01合并前审查测试
  2. 02查找注册和登录测试缺口
  3. 03确认测试能否发现缺陷

安装

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

$npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill test-quality-inspector

安装后在 Claude Code 中输入/test-quality-inspector确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.json
references/
assertion-quality.md
inspection-checklist.md
red-flags.md

详细介绍

示例测试检查报告

场景:用户注册功能

工程师的测试套件
# test_user_registration.py

def test_user_creation():
    """Test user creation"""
    user = create_user("[email protected]", "password123")
    assert user

def test_login():
    """Test login"""
    user = create_user("[email protected]", "password123")
    result = login("[email protected]", "password123")
    assert result

def test_duplicate_email():
    """Test duplicate email"""
    create_user("[email protected]", "password123")
    user2 = create_user("[email protected]", "password456")
    assert user2

QA 检查报告

测试 1:test_user_creation()

**声称的意图:**测试用户创建 **实际测试:**仅对象实例化

发现的问题

🔴 严重:毫无价值的断言

assert user  # Line 5
  • 数据损坏时也会通过
  • 电子邮件错误时也会通过
  • 密码哈希无效时也会通过
  • 数据库保存失败时也会通过

**思路调试:**包含垃圾数据的用户对象也能通过此测试。

🟠 高:缺少验证测试

  • 没有测试无效的电子邮件格式
  • 没有测试弱密码
  • 没有测试重复电子邮件(有单独的测试,但已损坏)
  • 没有测试电子邮件中的 SQL 注入

🟠 高:没有验证持久化

  • 测试未验证用户是否已保存到数据库
  • 测试未验证是否可以检索用户
  • 事务可能已回滚

🟡 中:测试名称含糊

  • “test_user_creation”没有描述预期结果
  • 更好的名称:“test_user_creation_with_valid_data_persists_to_database”
建议的改进
def test_user_creation_with_valid_data_persists_to_database():
    """Creating a user with valid email and password should:
    1. Create user object with correct attributes
    2. Save to database
    3. Hash password (not store plaintext)
    4. Set default role and active status
    """
    email = "[email protected]"
    password = "SecurePass123!"

    user = create_user(email, password)

    # Verify user object
    assert user.id is not None, "User should have an ID after creation"
    assert user.email == email, "Email should match input"
    assert user.is_active is True, "New users should be active by default"
    assert user.role == "user", "New users should have 'user' role"
    assert user.created_at is not None, "Created timestamp should be set"

    # Verify password is hashed, not plaintext
    assert user.password_hash != password, "Password should be hashed"
    assert len(user.password_hash) > 50, "Password hash should be substantial"

    # Verify persistence
    retrieved_user = User.get_by_email(email)
    assert retrieved_user is not None, "User should be retrievable from database"
    assert retrieved_user.id == user.id, "Retrieved user should match created user"

def test_user_creation_with_invalid_email_format_raises_validation_error():
    """Creating a user with malformed email should raise ValidationError"""
    invalid_emails = [
        "not-an-email",
        "@example.com",
        "test@",
        "test [email protected]",
        "",
    ]

    for invalid_email in invalid_emails:
        with pytest.raises(ValidationError) as exc:
            create_user(invalid_email, "password123")

        assert "email" in str(exc.value).lower()
        assert "invalid" in str(exc.value).lower()

def test_user_creation_with_weak_password_raises_validation_error():
    """Creating a user with weak password should raise ValidationError"""
    weak_passwords = [
        "123",          # Too short
        "password",     # No numbers
        "12345678",     # No letters
        "",             # Empty
    ]

    for weak_password in weak_passwords:
        with pytest.raises(ValidationError) as exc:
            create_user("[email protected]", weak_password)

        assert "password" in str(exc.value).lower()

风险等级: 🔴 严重 操作: ❌ 阻止——未测试核心功能 预计修复时间: 30 分钟


测试 2:test_login()

**声称的意图:**测试登录 **实际测试:**函数调用能够完成

发现的问题

🔴 严重:毫无价值的断言

assert result  # Line 11
  • 任何真值都会通过
  • 未验证会话/令牌
  • 未验证用户身份验证状态

🔴 严重:缺少反向测试

  • 没有测试错误密码
  • 没有测试不存在的用户
  • 没有测试已锁定的账户
  • 没有测试已过期的凭据

🟠 高:没有验证会话

  • 未验证身份验证令牌
  • 未验证会话过期时间
  • 未验证会话中的用户上下文

🟡 中:测试依赖于前一个测试

  • 在此测试中创建用户
  • 应使用 fixture 或 setup
  • 测试应相互独立
建议的改进
@pytest.fixture
def registered_user():
    """Fixture providing a registered user for login tests"""
    user = create_user("[email protected]", "SecurePass123!")
    yield user
    # Cleanup if needed
    User.delete(user.id)

def test_login_with_valid_credentials_returns_authenticated_session(registered_user):
    """Logging in with correct email and password should:
    1. Return authentication token/session
    2. Set authenticated state
    3. Include user context
    4. Set appropriate expiry
    """
    session = login(registered_user.email, "SecurePass123!")

    assert session is not None, "Login should return session"
    assert session.is_authenticated is True, "Session should be authenticated"
    assert session.user_id == registered_user.id, "Session should contain user ID"
    assert session.token is not None, "Session should have authentication token"
    assert session.expires_at > datetime.now(), "Session should have future expiry"
    assert (session.expires_at - datetime.now()).seconds >= 3600, "Session should last at least 1 hour"

def test_login_with_wrong_password_raises_authentication_error(registered_user):
    """Logging in with incorrect password should raise AuthenticationError"""
    with pytest.raises(AuthenticationError) as exc:
        login(registered_user.email, "WrongPassword")

    assert "Invalid credentials" in str(exc.value)
    assert "password" in str(exc.value).lower()

def test_login_with_nonexistent_email_raises_authentication_error():
    """Logging in with non-existent email should raise AuthenticationError"""
    with pytest.raises(AuthenticationError) as exc:
        login("[email protected]", "password")

    assert "Invalid credentials" in str(exc.value)
    # Note: Don't reveal if email exists (security)

def test_login_with_locked_account_raises_account_locked_error(registered_user):
    """Logging in to locked account should raise AccountLockedError"""
    lock_account(registered_user.id)

    with pytest.raises(AccountLockedError) as exc:
        login(registered_user.email, "SecurePass123!")

    assert registered_user.email in str(exc.value)

def test_login_with_empty_password_raises_validation_error(registered_user):
    """Logging in with empty password should raise ValidationError"""
    with pytest.raises(ValidationError) as exc:
        login(registered_user.email, "")

    assert "password" in str(exc.value).lower()
    assert "required" in str(exc.value).lower()

风险等级: 🔴 严重 操作: ❌ 阻止——实际上未测试身份验证 预计修复时间: 45 分钟


测试 3:test_duplicate_email()

**声称的意图:**测试重复电子邮件的处理 **实际测试:**第二个用户创建成功(错误!)

发现的问题

🔴 严重:测试方向相反

user2 = create_user("[email protected]", "password456")
assert user2  # Line 17
  • 此测试预期重复创建会成功
  • 它应该预期创建失败并报错
  • 本应失败的测试却通过了
  • 此测试验证的结果与所需结果相反

🔴 严重:造成虚假信心

  • 生产缺陷:允许使用重复的电子邮件
  • 测试声称会验证重复项防护
  • 测试实际上验证了重复项可用
  • QA 可能会误以为此问题已覆盖并批准

🟡 中:与其他测试存在相同的电子邮件问题

  • 如果修复为预期报错,则需要应用测试 1 的所有改进
建议的修复
def test_create_user_with_duplicate_email_raises_integrity_error():
    """Creating a user with an email that already exists should:
    1. Raise IntegrityError or ValidationError
    2. Not create duplicate user in database
    3. Preserve existing user data
    """
    email = "[email protected]"

    # Create first user
    user1 = create_user(email, "FirstPassword123!")
    initial_count = User.count()

    # Attempt to create duplicate
    with pytest.raises((IntegrityError, ValidationError)) as exc:
        create_user(email, "SecondPassword456!")

    assert "email" in str(exc.value).lower()
    assert "duplicate" in str(exc.value).lower() or "exists" in str(exc.value).lower()

    # Verify no new user created
    assert User.count() == initial_count, "User count should not increase"

    # Verify original user unchanged
    original_user = User.get_by_email(email)
    assert original_user.id == user1.id, "Original user should be intact"
    assert original_user.verify_password("FirstPassword123!"), "Original password should work"
    assert not original_user.verify_password("SecondPassword456!"), "New password should not work"

风险等级: 🔴 严重 操作: ❌ 阻止——测试验证的结果与需求相反 预计修复时间: 20 分钟


总结报告

总体评估

测试套件质量: 🔴 未通过

严重问题: 3

  • 测试 1:实际上未测试用户创建
  • 测试 2:实际上未测试身份验证
  • 测试 3:测试结果与需求相反

测试总数: 3 有效测试: 0 覆盖率: 高(声称如此) 保护: 无(实际情况)

风险评估

生产风险: 🔴 极高

当前测试套件无法提供任何保护,无法防范:

  • 用户创建时的数据损坏
  • 绕过身份验证
  • 重复电子邮件注册
  • 密码安全问题
  • 数据库完整性问题

信心等级: 0%——测试通过毫无意义

必需操作
立即执行(阻止合并)
  1. 使用正确的断言重写全部三个测试
  2. 添加反向测试用例(需要 12 个以上测试)
  3. 验证测试能捕获故意引入的缺陷
  4. 添加用于管理测试用户的 fixture
后续工作(完成审核的必要条件)
  1. 添加边界情况测试(至少额外 15 项)
  2. 为完整注册流程添加集成测试
  3. 添加安全测试(SQL 注入、XSS 等)
  4. 为注册端点添加性能测试
预估时间
  • 修复关键问题:2-3 小时
  • 完成测试套件:1 天
  • 审查和迭代:0.5 天

总计: 1.5-2 天,以完善测试覆盖率

建议

❌ 阻止合并

不要批准此 PR。测试会造成虚假的信心,并掩盖关键错误。

证据:

  • 即使功能完全损坏,所有测试也会通过
  • 重复邮箱测试验证的结果与需求相反
  • 没有验证任何实际行为

后续步骤:

  1. 工程师按照上述示例重写测试
  2. QA 重新检查重写后的测试
  3. QA 验证测试能捕获故意引入的错误
  4. 只有完成以上步骤后,才能批准合并

给工程师的经验教训

问题所在
  1. 先写代码再写测试——导致测试只确认代码能运行
  2. 断言过于薄弱——“assert x”毫无证明作用
  3. 没有进行思维调试——未验证测试能否捕获错误
  4. 没有负向测试——只测试了正常路径
  5. 误解了重复项测试——测试验证的结果恰好相反
如何改进
  1. 先写测试(TDD)——避免这些问题
  2. 使用具体的断言——验证确切的值
  3. 进行思维调试——破坏代码,确保测试失败
  4. 明确测试失败情况——每个成功情况都需要对应的失败测试
  5. 仔细阅读测试名称——测试你声称要测试的内容
TDD 本可避免此问题

如果先编写测试:

# Write this FIRST (it will fail):
def test_user_creation_with_valid_data_persists_to_database():
    user = create_user("[email protected]", "password")
    assert user.email == "[email protected]"  # Will fail until create_user works
    ...

# Then implement create_user to make it pass

请参阅测试驱动开发技能,了解完整的 TDD 工作流(可在技能库中找到,提供全面的 TDD 指导)。


签核

QA 检查员: [你的姓名] 日期: [日期] 状态: ❌ 已拒绝 原因: 测试毫无保障作用,必须重写 是否需要重新检查: 是


这才是彻底检查测试的方式。现在发现这些问题,总比等到上线后再发现要好。

评论

支持 Markdown