assertion-quality
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAssertion Diversity Analysis
断言多样性分析
Analyze .NET test code to measure how varied and meaningful the assertions are. Produce a metrics report that reveals whether tests verify different facets of correctness — not just "output equals X" but also structure, exceptions, state transitions, side effects, and invariants.
分析.NET测试代码,衡量断言的多样性与意义。生成指标报告,揭示测试是否验证了正确性的不同维度——不仅是“输出等于X”,还包括结构、异常、状态转换、副作用和不变量。
Why Assertion Diversity Matters
断言多样性的重要性
Low assertion diversity signals shallow testing. Tests may pass while bugs hide in unasserted logic. Common symptoms:
| Problem | Symptom | Consequence |
|---|---|---|
| Trivial assertions | | Test passes but doesn't verify correctness |
| Single-value obsession | Always check one field or return value | Bugs in unasserted logic slip through |
| No negative assertions | Never check what shouldn't happen | Regressions sneak in through false positives |
| No state checks | Don't verify object state changes | Missed side-effects or lifecycle issues |
| No structural checks | Only assert top-level value | Bugs in nested objects go unnoticed |
| Assertion-free tests | Tests that call but don't verify | Code coverage lies; false security |
低断言多样性意味着浅层测试。测试可能通过,但Bug仍隐藏在未被断言的逻辑中。常见症状:
| 问题 | 表现 | 后果 |
|---|---|---|
| 无实际意义的断言 | 仅使用 | 测试通过但未验证正确性 |
| 单一值依赖 | 始终只检查一个字段或返回值 | 未被断言的逻辑中的Bug会被遗漏 |
| 无否定断言 | 从不验证不应发生的情况 | 回归问题会通过假阳性悄悄混入 |
| 无状态检查 | 不验证对象状态变化 | 遗漏副作用或生命周期问题 |
| 无结构检查 | 仅断言顶层值 | 嵌套对象中的Bug无法被发现 |
| 无断言测试 | 仅调用代码但不做验证 | 代码覆盖率存在误导,带来虚假安全感 |
When to Use
适用场景
- User asks to evaluate assertion quality or depth
- User asks "are my tests actually testing anything meaningful?"
- User wants to know if test assertions are too shallow or trivial
- User asks for assertion coverage metrics or diversity analysis
- User suspects tests give false confidence despite passing
- 用户请求评估断言的质量或深度
- 用户询问“我的测试是否真的测试了有意义的内容?”
- 用户想了解测试断言是否过于浅层或无实际意义
- 用户请求断言覆盖指标或多样性分析
- 用户怀疑测试虽通过但提供了虚假信心
When Not to Use
不适用场景
- User wants to write new tests (use )
writing-mstest-tests - User wants to detect anti-patterns beyond assertions (use )
test-anti-patterns - User wants to fix or rewrite assertions (help them directly)
- User asks about code coverage percentages (out of scope — this analyzes assertion quality, not line coverage)
- 用户想要编写新测试(请使用)
writing-mstest-tests - 用户想要检测断言之外的反模式(请使用)
test-anti-patterns - 用户想要修复或重写断言(直接提供帮助)
- 用户询问代码覆盖率百分比(超出范围——此工具分析断言质量,而非行覆盖率)
Inputs
输入项
| Input | Required | Description |
|---|---|---|
| Test code | Yes | One or more test files or a test project directory to analyze |
| Production code | No | The code under test, to evaluate whether assertions cover the important behaviors |
| 输入 | 是否必填 | 描述 |
|---|---|---|
| 测试代码 | 是 | 要分析的一个或多个测试文件,或测试项目目录 |
| 生产代码 | 否 | 被测代码,用于评估断言是否覆盖了重要行为 |
Workflow
工作流程
Step 1: Gather the test code
步骤1:收集测试代码
Read all test files the user provides. If the user points to a directory or project, scan for all test files — see the skill for framework-specific markers.
dotnet-test-frameworks读取用户提供的所有测试文件。如果用户指向目录或项目,扫描所有测试文件——可参考技能中针对各框架的标记规则。
dotnet-test-frameworksStep 2: Classify every assertion
步骤2:分类所有断言
For each test method, identify all assertions and classify them into these categories:
| Category | Examples | What it verifies |
|---|---|---|
| Equality | | Return value matches expected |
| Boolean | | Condition holds |
| Null checks | | Presence/absence of value |
| Exception | | Error handling behavior |
| Type checks | | Runtime type correctness |
| String | | Text content and format |
| Collection | | Collection contents and structure |
| Comparison | | Ordering and magnitude |
| Approximate | | Floating-point or tolerance-based |
| Negative | | What should NOT happen |
| State/Side-effect | Assertions on object properties after mutation, verifying mock calls | State transitions and side effects |
| Structural/Deep | Assertions on nested properties, serialized forms, complex objects | Deep object correctness |
A single assertion can belong to multiple categories (e.g., is both Equality and Negative).
Assert.AreNotEqual针对每个测试方法,识别所有断言并将其分类为以下类别:
| 类别 | 示例 | 验证内容 |
|---|---|---|
| 相等性 | | 返回值与预期匹配 |
| 布尔值 | | 条件成立 |
| 空值检查 | | 值的存在/缺失 |
| 异常 | | 错误处理行为 |
| 类型检查 | | 运行时类型正确性 |
| 字符串 | | 文本内容与格式 |
| 集合 | | 集合内容与结构 |
| 比较 | | 顺序与量级 |
| 近似值 | | 浮点型或基于容差的验证 |
| 否定 | | 不应发生的情况 |
| 状态/副作用 | 对对象突变后的属性进行断言、验证Mock调用 | 状态转换与副作用 |
| 结构/深度 | 对嵌套属性、序列化形式、复杂对象进行断言 | 对象深度正确性 |
单个断言可属于多个类别(例如同时属于相等性和否定类别)。
Assert.AreNotEqualStep 3: Compute metrics
步骤3:计算指标
Calculate these metrics for the test suite:
为测试套件计算以下指标:
Per-test metrics
单测试指标
- Assertion count: Number of assertions in each test method
- Assertion categories: Which categories each test uses
- 断言数量:每个测试方法中的断言数量
- 断言类别:每个测试使用的类别
Suite-wide metrics
套件级指标
- Average assertions per test: Total assertions / total test methods
- Assertion type spread: Number of distinct assertion categories used across the suite (out of 12)
- Tests with zero assertions: Count and percentage of test methods with no assertions at all
- Tests with only trivial assertions: Count and percentage of tests where every assertion is only a null check or — trivial means no meaningful value verification
Assert.IsTrue(true) - Tests with self-referential assertions: Count and percentage of tests whose assertions compare an input to a round-tripped or identity-transformed version of itself (e.g., ) or assert a field against itself (
Assert.AreEqual(input, Parse(input.ToString()))). These are tautological — they verify the plumbing, not the behavior.Assert.AreEqual(dto.Name, dto.Name) - Tests with negative assertions: Count and percentage (target: at least 10% of tests should verify what should NOT happen)
- Tests with exception assertions: Count and percentage
- Tests with state/side-effect assertions: Count and percentage
- Tests with structural/deep assertions: Count and percentage
- Single-category tests: Count and percentage of tests that use only one assertion category
- 单测试平均断言数:总断言数 / 总测试方法数
- 断言类型覆盖率:套件中使用的不同断言类别数量(共12类)
- 无断言测试:完全无断言的测试方法数量及占比
- 仅含无实际意义断言的测试:所有断言仅为空值检查或的测试方法数量及占比——无实际意义指未进行有价值的验证
Assert.IsTrue(true) - 自引用断言测试:断言将输入与往返转换或身份转换后的自身进行比较(例如),或断言字段与自身比较(
Assert.AreEqual(input, Parse(input.ToString())))的测试方法数量及占比。这类断言是同义反复的——仅验证了流程,而非行为Assert.AreEqual(dto.Name, dto.Name) - 含否定断言的测试:数量及占比(目标:至少10%的测试应验证不应发生的情况)
- 含异常断言的测试:数量及占比
- 含状态/副作用断言的测试:数量及占比
- 含结构/深度断言的测试:数量及占比
- 单类别断言测试:仅使用一种断言类别的测试方法数量及占比
Step 4: Apply calibration rules
步骤4:应用校准规则
Before reporting, calibrate findings:
- Trivial means truly trivial. alone is trivial. But
Assert.IsNotNull(result)followed byAssert.IsNotNull(result)is not — the null check is a guard before the real assertion. Only flag a test as "trivial" if it has no meaningful value assertions.Assert.AreEqual(expected, result.Value) - Boolean assertions checking meaningful conditions are not trivial. checks a specific property — it's a Boolean assertion, not a trivial one.
Assert.IsTrue(result.IsValid)is trivial.Assert.IsTrue(true) - Consider the test's intent. A test for a void method that verifies state change on a dependency is legitimate even if it only uses .
Assert.IsTrue - Exception tests are inherently low-assertion-count. may be the only assertion — that's fine for exception-focused tests. Don't penalize them for low assertion count.
Assert.ThrowsException<T>(() => ...) - Don't conflate diversity with volume. A test with 20 calls has high volume but low diversity. A test with one equality, one null check, and one exception assertion has low volume but good diversity.
Assert.AreEqual - Self-referential assertions are not meaningful equality checks. looks like a real equality assertion but is tautological when the operation under test is expected to be identity. Flag these separately from normal equality assertions. If the test's purpose is to verify a round-trip (serialize/deserialize, encode/decode), the assertion is valid — but it should be accompanied by assertions on non-trivial inputs that exercise the transformation.
Assert.AreEqual(input, roundTrip(input)) - If assertions are well-diversified, say so. A report concluding the suite has good diversity is perfectly valid.
报告前,校准分析结果:
- 无实际意义指真正无价值。仅是无实际意义的,但
Assert.IsNotNull(result)之后跟随Assert.IsNotNull(result)则不属于——空值检查是真实断言前的防护。仅当测试无有价值的验证时,才标记为“无实际意义”Assert.AreEqual(expected, result.Value) - 验证有意义条件的布尔断言不属于无实际意义。检查特定属性——这是布尔断言,而非无实际意义的断言。
Assert.IsTrue(result.IsValid)才是无实际意义的Assert.IsTrue(true) - 考虑测试意图。针对void方法、验证依赖项状态变化的测试,即使仅使用也是合理的
Assert.IsTrue - 异常测试本质上断言数量少。可能是唯一的断言——这对于异常聚焦的测试是正常的。不要因断言数量少而惩罚这类测试
Assert.ThrowsException<T>(() => ...) - 不要将多样性与数量混淆。包含20个调用的测试数量多但多样性低。包含一个相等性、一个空值检查和一个异常断言的测试数量少但多样性好
Assert.AreEqual - 自引用断言不是有意义的相等性检查。看似真实的相等性断言,但当被测操作预期为身份转换时,它是同义反复的。将这些与正常相等性断言分开标记。如果测试的目的是验证往返转换(序列化/反序列化、编码/解码),则该断言是有效的——但应伴随针对非平凡输入的断言以验证转换逻辑
Assert.AreEqual(input, roundTrip(input)) - 如果断言多样性良好,直接说明。结论为套件多样性良好的报告是完全有效的
Step 5: Report findings
步骤5:报告分析结果
Present the analysis in this structure:
-
Summary Dashboard — A quick-reference table of key metrics:
| Metric | Value | Assessment | |-------------------------------|--------|------------| | Total tests | 25 | — | | Average assertions per test | 2.4 | Moderate | | Assertion type spread | 5/12 | Low | | Tests with zero assertions | 3 (12%)| Concerning | | Tests with only trivial asserts | 4 (16%)| Acceptable | | Tests with negative assertions | 2 (8%) | Below target | | Single-category tests | 15 (60%)| High | -
Category Breakdown — For each assertion category, show:
- How many tests use it
- Representative examples from the code
- Whether it's overused or underused relative to the code under test
-
Gap Analysis — Based on the production code (if available), identify:
- Behaviors that are tested but only with equality checks
- Error paths with no exception assertions
- State-changing methods with no state verification
- Collections returned but never checked for contents
-
Recommendations — Prioritized list of improvements:
- Which tests would benefit most from additional assertion types
- Which assertion categories are missing and why they matter
- Concrete examples of assertions that could be added
-
Assertion-free tests — If any exist, list each one with its method name and what it appears to be testing, so the user can decide whether to add assertions or mark them as intentional smoke tests.
按以下结构呈现分析内容:
-
概览仪表盘——关键指标的速查表:
| 指标 | 数值 | 评估 | |-------------------------------|--------|------------| | 总测试数 | 25 | — | | 单测试平均断言数 | 2.4 | 中等 | | 断言类型覆盖率 | 5/12 | 低 | | 无断言测试数 | 3 (12%)| 需关注 | | 仅含无实际意义断言的测试 | 4 (16%)| 可接受 | | 含否定断言的测试 | 2 (8%) | 低于目标 | | 单类别断言测试 | 15 (60%)| 高 | -
类别细分——针对每个断言类别,展示:
- 使用该类别的测试数量
- 代码中的代表性示例
- 相对于被测代码,该类别是否被过度使用或使用不足
-
差距分析——基于生产代码(若提供),识别:
- 已测试但仅使用相等性检查的行为
- 无异常断言的错误路径
- 无状态验证的状态变更方法
- 返回但从未检查内容的集合
-
建议——按优先级排序的改进列表:
- 哪些测试最受益于添加其他类型的断言
- 缺失哪些断言类别及其重要性
- 可添加的具体断言示例
-
无断言测试——如果存在,列出每个测试的方法名称及其看似要测试的内容,以便用户决定是否添加断言或标记为有意的冒烟测试
Validation
验证项
- Every assertion in the test suite was classified into at least one category
- Metrics are computed correctly (counts add up)
- Trivial-assertion tests are correctly identified (not over-flagged)
- Exception tests are not penalized for low assertion count
- Boolean assertions on meaningful properties are not classified as trivial
- Recommendations are concrete (name specific test methods and suggest specific assertion types)
- If the suite has good diversity, the report acknowledges this
- 测试套件中的每个断言至少被分类到一个类别
- 指标计算正确(计数总和匹配)
- 含无实际意义断言的测试被正确识别(未过度标记)
- 异常测试未因断言数量少而被惩罚
- 验证有意义属性的布尔断言未被分类为无实际意义
- 建议具体(指明特定测试方法并建议特定断言类型)
- 如果套件多样性良好,报告中会提及
Common Pitfalls
常见陷阱
| Pitfall | Solution |
|---|---|
| Penalizing exception tests for low assertion count | Exception assertions are complete on their own — skip count warnings for these |
| Flagging null checks before value checks as trivial | Only flag tests where the null check is the ONLY assertion |
Counting | Only |
| Ignoring framework differences | MSTest uses |
| Recommending diversity for diversity's sake | Only suggest adding assertion types that would catch real bugs in the code under test |
| Missing implicit assertions | |
| 陷阱 | 解决方案 |
|---|---|
| 因断言数量少惩罚异常测试 | 异常断言本身已完整——跳过对此类测试的数量警告 |
| 将值检查前的空值标记为无实际意义 | 仅当空值检查是唯一断言时,才标记测试为无实际意义 |
将 | 仅 |
| 忽略框架差异 | MSTest使用 |
| 为了多样性而追求多样性 | 仅建议添加能发现被测代码中真实Bug的断言类型 |
| 遗漏隐式断言 | |