[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DZPBST26XPB6GX396JXZFD":3,"skill-same:01M6DZPBST26XPB6GX396JXZFD":99},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":55,"commitSha":56,"skillDirectory":57,"forksCount":58,"repositoryHtmlUrl":59,"repositoryHomepageUrl":60,"ownerHtmlUrl":61,"ownerBlogUrl":62,"ownerDescription":63,"updatedAt":64,"install":65,"resources":78},"01M6DZPBST26XPB6GX396JXZFD","test-anti-patterns",null,"Audit a test file or suite; produce a severity-ranked diagnostic report. ALWAYS USE for tests that verify nothing, missing\u002Ftautological assertions, swallowed\u002Fbroad exceptions, flaky\u002Forder-dependent tests, duplication, or magic values. Polyglot. DO NOT USE for direct edits: writing-mstest-tests owns supplied MSTest assertions\u002Fattributes\u002Flifecycle; code-testing owns new tests. Exclude running tests, migration, assertion metrics (assertion-quality), raw .NET coverage collection (run-tests), non-.NET coverage collection\u002Fanalysis (native tooling), project-wide .NET coverage\u002FCRAP (coverage-analysis), named-target .NET CRAP (crap-score), behavioral\u002Fpseudo-mutation gaps (test-gap-analysis), test-mix\u002F happy-vs-error classification and trait distributions (test-tagging), or the testsmells.org catalog (test-smell-detection).","managedcode\u002Fdotnet-skills","managedcode","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F78253589?v=4","01M6DYDJR3EG8KB5G4PC65XCTT","Managed Code","01M6DYDJX0ZT432N04NYJW89DN",514,"MIT","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet-test\u002Fskills\u002Ftest-anti-patterns\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"test","test anti patterns","anti patterns","patterns","coverage","new","mix","attributes","native","error","distributions","mutation","project","duplication","behavioral","testing","direct","analysis","collection","gap-analysis","audit","metrics","code","run","writing","2026-11-10 10:12:30",false,"---\nname: test-anti-patterns\ndescription: >\n  Audit a test file or suite; produce a severity-ranked diagnostic report.\n  ALWAYS USE for tests that verify nothing, missing\u002Ftautological\n  assertions, swallowed\u002Fbroad exceptions, flaky\u002Forder-dependent tests,\n  duplication, or magic values. Polyglot. DO NOT USE for direct edits:\n  writing-mstest-tests owns supplied MSTest assertions\u002Fattributes\u002Flifecycle;\n  code-testing owns new tests. Exclude running tests, migration, assertion\n  metrics (assertion-quality), raw .NET coverage collection (run-tests),\n  non-.NET coverage collection\u002Fanalysis (native tooling), project-wide .NET coverage\u002FCRAP\n  (coverage-analysis), named-target .NET CRAP\n  (crap-score), behavioral\u002Fpseudo-mutation gaps (test-gap-analysis), test-mix\u002F\n  happy-vs-error classification and trait distributions (test-tagging), or the\n  testsmells.org catalog (test-smell-detection).\nlicense: MIT\n---\n\n# Test Anti-Pattern Detection\n\nQuick, pragmatic analysis of test code in any supported language for anti-patterns and quality issues that undermine test reliability, maintainability, and diagnostic value.\n\n> **Language-specific guidance**: Read the caller-provided or runtime-listed\n> `test-analysis-extensions` catalog and its matching language file when needed.\n> Do not invoke the reference-only\n> helper. If its files are absent, use the built-in framework rules below.\n\n## When to Use\n\n- User asks to review test quality or find test smells\n- User wants to know why tests are flaky or unreliable\n- User asks \"are my tests good?\" or \"what's wrong with my tests?\"\n- User requests a test audit or test code review\n- User wants diagnostic findings before deciding what to improve\n\n## When Not to Use\n\n- User wants to write new tests from scratch (use `code-testing`)\n- User wants direct implementation fixes rather than a diagnostic review (use the relevant write\u002Fedit skill)\n- User asks to fix swapped `Assert.AreEqual` argument order in MSTest (use `writing-mstest-tests`)\n- User asks to convert MSTest `DynamicData` from `IEnumerable\u003Cobject[]>` to `ValueTuple` (use `writing-mstest-tests`)\n- User wants to run or execute tests (use `run-tests` for .NET)\n- User wants to migrate between test frameworks or versions (use migration skills)\n- User wants raw .NET coverage collection (use `run-tests`), non-.NET coverage collection or analysis (use native tooling), project-wide .NET coverage\u002FCRAP metrics (use `coverage-analysis`), or named-target .NET CRAP (use `crap-score`)\n- User asks whether tests would catch a bug or wants behavioral\u002Fpseudo-mutation gaps (use `test-gap-analysis`)\n- User wants test-mix or happy-vs-error-path classification, standardized tagging, or trait\u002Fcategory distributions (use `test-tagging`)\n- User wants a deep formal test smell audit with academic taxonomy and extended catalog (use `test-smell-detection`)\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Test scope | No | Test files, classes, directory, or project to analyze. Discover from the current workspace when omitted. |\n| Production code | No | The code under test, for context on what tests should verify |\n| Specific concern | No | A focused area like \"flakiness\" or \"naming\" to narrow the review |\n\n## Workflow\n\n### Step 1: Detect language and load extension\n\nResolve the named test path from the current workspace before asking for input.\nWhen no path is supplied, discover test files under the current directory using\nthe repository manifests and conventional test markers. The skill context's\n`Base directory` is documentation storage, not the user's workspace; never\nresolve target files relative to it.\n\nIf one reader says a path is missing but a workspace glob\u002Fsearch finds it,\nnormalize that exact path and retry. Use a shell text reader (`sed`\u002F`cat` on\nUnix, `Get-Content` on PowerShell) only for a confirmed reader availability,\ntransport, or path-normalization failure and only after verifying the canonical\npath remains inside the current workspace. Stop on content-exclusion,\npermission\u002Fpolicy, workspace-boundary, or unknown failures. Audit any discovered\nfile that a permitted reader can access; never ask the user to paste it. If\nevery permitted reader fails, report the exact blocker without bypassing\nsecurity boundaries.\n\nIdentify the language and framework. Read the matching bundled reference when\nneeded, resolving its filename relative to the supplied catalog.\nCheck only that known reference directory, never installation directories.\nIf unavailable, use the catalog below and report the reference limitation.\n\n### Step 2: Gather the test code\n\nInventory the resolved scope before reading bodies. For one file or class, read\nthat scope directly. For a project or suite, discover test files once, batch\nindependent reads where tools allow, and stop when every discovered test and\nclass-level fixture has a ledger disposition.\n\nUse extension discovery markers when loaded; otherwise use the built-in markers\nin this skill (attributes such as `[TestClass]`\u002F`[Fact]`\u002F`[Test]`,\n`test_*.py`, `*.test.*`, `*_test.go`, `*_spec.rb`, `#[test]`,\n`*.Tests.ps1`, `TEST(...)`, and `TEST_CASE(...)`).\n\nDo not read unrelated production code wholesale. Open the production symbol\ncorresponding to every suspicious test needed to decide whether an assertion,\ntransformation, identity contract, or adjacent gap is real. For a systematic\nfacade\u002Fsurface-area pattern, every invoked member is relevant: read the entire\nsmall production type or inspect each invoked member, then map each weak test to\nthe exact observable result, exception, state change, or boundary it should\nverify.\n\n### Step 3: Scan for anti-patterns\n\nCheck each test file against the anti-pattern catalog below. Report findings\ngrouped by severity. Use extension mappings when loaded; otherwise use the\ncross-framework examples in the catalog.\n\nBefore drafting the report, make a private completeness ledger with one row for\nevery test method and every class-level fixture\u002Fresource. Record its oracle (or\nabsence), exception handling, state\u002Ftime dependencies, concurrency safety,\nprecondition\u002Fassertion order, and disposition. Do not publish until every row is\neither attached to a finding or explicitly judged sound. In particular:\n\n- `actual != oldValue` is a weak mutation oracle: it accepts every wrong new\n  value. Require the exact expected value.\n- Include unused or undisposed class-level resources; method-only scans miss\n  fields such as a static `HttpClient`.\n- Treat an unsynchronized static\u002Fglobal collection as both order-coupled and\n  parallel-unsafe when tests read and write it. Also flag dereferencing a\n  nullable result before the assertion intended to prove it non-null.\n- When production code is supplied, note obvious untested contracts adjacent to\n  a finding, but do not perform exhaustive branch or mutation analysis. Route\n  that broader question to `test-gap-analysis`.\n\n#### Critical -- Tests that give false confidence\n\n| Anti-Pattern | What to Look For |\n|---|---|\n| **No assertions** | Test methods that execute code but never assert anything. A passing test without assertions proves nothing. In .NET look for missing `Assert.*`; in pytest a function with no `assert` and no `pytest.raises`; in Jest no `expect(...)`; in JUnit no `assert*`\u002F`assertThat`; in Go a test that never calls `t.Error*`, `t.Fatal*`, or testify; in RSpec a block with no `expect`; in Pester no `Should`. Mock-call verifications (`verify(mock)`, `expect(mock).toHaveBeenCalled`, `Should -Invoke`) are real assertions. |\n| **Missing await on async assertions (JS\u002FTS, .NET, Python, Kotlin, Swift)** | `expect(promise).resolves.toBe(x)` without `await`\u002F`return`, `pytest-asyncio` test with un-awaited coroutine, `async Task` xUnit test calling `Assert.ThrowsAsync` without `await`, Kotest suspending test without `runTest`, Swift Testing async test without `await`. These tests silently pass even when the underlying assertion would have failed. |\n| **Coverage touching** | Test class that methodically calls every public member on a type — often in alphabetical or declaration order — without asserting meaningful outcomes. Each test typically does `var result = sut.MethodName(...)` (or `result = sut.method_name(...)`, `sut.methodName()`, `sut.MethodName(t)`) with no assertion, or only a trivial null\u002FNone\u002Fnil check. The intent is to inflate code-coverage metrics rather than verify behavior. Distinct from a single assertion-free test: the pattern is *systematic* coverage of the surface area with no real verification. |\n| **Self-referential assertion** | The expected value is computed from the same actual value, such as `Assert.AreEqual(dto.Name, dto.Name)`, `Assert.AreEqual(result, result)`, or equivalents. Do not apply this label merely because a valid identity, clone, serialization, or round-trip contract compares output with input: those assertions can fail. Instead check whether the input exercises a transformation and whether independently known representation, field, reference-identity, or invalid-input assertions are missing. |\n| **Swallowed exceptions** | `try { ... } catch { }`, `catch (Exception)` without rethrowing or asserting (.NET); bare `except:` or `except Exception:` with `pass` (Python); `try { ... } catch (e) {}` (JS\u002FTS\u002FJava); `defer recover()` without re-panic and no assertion (Go); `rescue StandardError` with no assertion (Ruby); `Result::unwrap_or(...)` swallowing errors in a test (Rust); empty `catch` block (Kotlin\u002FSwift). |\n| **Assert in catch block only** | `try { Act(); } catch (Exception ex) { Assert.Fail(ex.Message); }` (and equivalents in other languages) -- use `Assert.ThrowsException` \u002F `pytest.raises` \u002F `expect(fn).toThrow` \u002F `assertThrows` \u002F `assert.Error(t, err)` \u002F `#[should_panic]` \u002F `Should -Throw` \u002F `EXPECT_THROW` instead. The test passes when no exception is thrown even if the result is wrong. |\n| **Always-true assertions** | `Assert.IsTrue(true)`, `Assert.AreEqual(x, x)`, `assert True`, `expect(true).toBe(true)`, `assert.True(t, true)`, `assert!(true)`, or conditions that can never fail. |\n| **Commented-out assertions** | Assertions that were disabled but the test still runs, giving the illusion of coverage. |\n\n#### High -- Tests likely to cause pain\n\n| Anti-Pattern | What to Look For |\n|---|---|\n| **Flakiness indicators** | Wall-clock sleeps\u002Fwaits used for synchronization: `Thread.Sleep` \u002F `Task.Delay` (.NET), `time.sleep` (Python), `setTimeout` \u002F `await new Promise(r => setTimeout(...))` (JS\u002FTS), `Thread.sleep` (Java\u002FKotlin), `time.Sleep` (Go), `sleep` (Ruby\u002FBash), `std::thread::sleep` (Rust), `Start-Sleep` (Pester), `std::this_thread::sleep_for` (C++). Wall-clock reads without abstraction: `DateTime.Now`\u002F`UtcNow`, `datetime.now()`\u002F`datetime.utcnow()`, `Date.now()` \u002F `new Date()`, `System.currentTimeMillis()`, `time.Now()`, `Time.now`, `Instant::now()`, `Date()`\u002F`Date.now`, `Get-Date`, `std::chrono::system_clock::now`. Unseeded randomness: `new Random()`, `random.random()`\u002F`random.randint()`, `Math.random()`, `new Random()` (Java\u002FKotlin), `rand.Int()` without seed, `rand` (Ruby), `rand::random()` (Rust). Environment-dependent paths (hard-coded `C:\\...`, `\u002Ftmp\u002F...`, network hosts). |\n| **Test ordering dependency** | Static\u002Fglobal mutable state modified across tests; setup that doesn't fully reset state (`[TestInitialize]`, `setUp`, `beforeEach`, `before(:each)`, `BeforeEach`, `t.Cleanup`); tests that fail when run individually but pass in suite (or vice versa). Examples per language: `static` fields (.NET\u002FJava), module-level globals (Python), top-level `let`\u002F`const` in test file (JS\u002FTS), `var` package globals (Go), class variables (Ruby), `static mut`\u002F`lazy_static!`\u002F`OnceCell` (Rust), `$script:` variables (PowerShell). |\n| **Over-mocking** | More mock setup lines than actual test logic. Verifying exact call sequences on mocks rather than outcomes. Mocking types the test owns. Per language: Moq\u002FNSubstitute\u002FFakeItEasy (.NET), `unittest.mock` \u002F `pytest-mock` (Python), Jest auto-mocks \u002F Sinon (JS\u002FTS), Mockito\u002FPowerMock (Java), gomock\u002Ftestify mock (Go), RSpec mocks\u002Fmocha (Ruby), `mockall` (Rust), MockK (Kotlin), `Mock` cmdlet (Pester), gmock (C++). For a deep mock audit in .NET, use `exp-mock-usage-analysis`. |\n| **Implementation coupling** | Testing private methods via reflection (`MethodInfo.Invoke`, `getattr` in Python, `(thing as any)` in TS, `Field.setAccessible(true)` in Java, `Object#send` in Ruby, internal `pub(crate)` access in Rust). Asserting on internal state instead of observable behavior. Verifying exact method call counts on collaborators instead of business outcomes. |\n| **Broad exception assertions** | `Assert.ThrowsException\u003CException>(...)` (.NET) \u002F `pytest.raises(Exception)` \u002F `expect(fn).toThrow(Error)` without a message matcher \u002F `assertThrows(Exception.class, ...)` (Java) \u002F `assert.Error(t, err)` without checking the kind \u002F `expect { ... }.to raise_error` without class (RSpec) \u002F `#[should_panic]` without `expected = \"...\"` \u002F `Should -Throw` without `-ExpectedMessage` \u002F `EXPECT_ANY_THROW` instead of `EXPECT_THROW(stmt, SpecificType)`. |\n| **Weak transformation oracle** | A normalization, casing, trimming, mapping, or conversion test supplies an input already in the expected form, so a no-op implementation passes even though the assertion may catch other defects. Use an input that must change and assert an independently derived expected value. A producer\u002Fconsumer round trip is useful but does not replace an independent format assertion when both sides could share the same defect. |\n\n#### Medium -- Maintainability and clarity issues\n\n| Anti-Pattern | What to Look For |\n|---|---|\n| **Poor naming** | Test names like `Test1`, `TestMethod`, or `test` that don't describe the scenario or outcome. Use the loaded extension when available; otherwise follow the existing naming convention in the same suite. |\n| **Magic values** | Unexplained numbers or strings in arrange\u002Fassert: `Assert.AreEqual(42, result)` \u002F `assert result == 42` \u002F `expect(result).toBe(42)` -- what does 42 mean? |\n| **Duplicate tests** | Three or more test methods with near-identical bodies that differ only in a single input value. Should be parametrized: `[DataRow]`\u002F`[Theory]`\u002F`[TestCase]` (.NET), `@pytest.mark.parametrize` (pytest), `test.each` \u002F `it.each` (Jest\u002FVitest), `@ParameterizedTest` + `@ValueSource` (JUnit 5), `@DataProvider` (TestNG), Go table-driven tests, `where` \u002F shared examples (RSpec), `#[rstest]` (Rust), `@ParameterizedTest` + `@MethodSource` (Kotlin), `-ForEach` \u002F `-TestCases` (Pester), `INSTANTIATE_TEST_SUITE_P` (GoogleTest), `SECTION` \u002F `GENERATE` (Catch2), `TEST_CASE_TEMPLATE` (doctest). For a detailed duplication analysis in .NET, use `exp-test-maintainability`. Note: Two tests covering distinct boundary conditions (e.g., zero vs. negative) are NOT duplicates -- separate tests for different edge cases provide clearer failure diagnostics and are a valid practice. |\n| **Giant tests** | Test methods exceeding ~30 lines or testing multiple behaviors at once. Hard to diagnose when they fail. |\n| **Assertion messages that repeat the assertion** | `Assert.AreEqual(expected, actual, \"Expected and actual are not equal\")` \u002F `assert x == y, \"x is not equal to y\"` \u002F `assertEquals(x, y, \"values not equal\")` add no information. Messages should describe the business meaning. |\n| **Missing AAA \u002F Given-When-Then separation** | Arrange\u002FAct\u002FAssert (or Given\u002FWhen\u002FThen for BDD frameworks like RSpec, Kotest behavior specs, Pester) phases are interleaved or indistinguishable. |\n\n#### Low -- Style and hygiene\n\n| Anti-Pattern | What to Look For |\n|---|---|\n| **Unused test infrastructure** | Setup\u002Fteardown hooks that do nothing — `[TestInitialize]`\u002F`[SetUp]`\u002F`[BeforeEach]`, `setUp`\u002F`@BeforeEach`\u002F`@BeforeAll`, `beforeEach`\u002F`beforeAll`, `before(:each)`\u002F`before(:all)`, `BeforeEach`\u002F`BeforeAll` (Pester), `setUpWithError` (XCTest) — and test helper methods that are never called. |\n| **Unmanaged resources** | Test creates disposable\u002Fcloseable resources without cleanup: `HttpClient`\u002F`Stream` without `using` (.NET), file\u002Fconnection without `with` block or `try\u002Ffinally` (Python), `FileInputStream` without `try-with-resources` (Java), `defer file.Close()` missing (Go), connection without `ensure` (Ruby), `Drop` not relied on \u002F forgotten `close` (Rust), missing teardown for temp files \u002F DBs in any language. |\n| **Print debugging** | Leftover `Console.WriteLine` \u002F `Debug.WriteLine` \u002F `print()` \u002F `console.log` \u002F `System.out.println` \u002F `fmt.Println` \u002F `puts` \u002F `dbg!` \u002F `Write-Host` \u002F `std::cout` statements used during test development. |\n| **Inconsistent naming convention** | Mix of naming styles in the same test class\u002Fmodule\u002Ffile (e.g., some use `Method_Scenario_Expected`, others use `ShouldDoSomething`). |\n\n### Step 4: Calibrate severity honestly\n\nBefore reporting, re-check each finding against these severity rules:\n\n- **Critical\u002FHigh**: Only for issues that cause tests to give false confidence or be unreliable. A test that always passes regardless of correctness is Critical. Shared mutable state is High when it is a latent isolation risk, but **Critical when the user reports actual order-dependent failures or the code proves one test requires another to run first**. Missing-await on async assertions is Critical (silent pass).\n- **Medium**: Only for issues that actively harm maintainability -- 5+ nearly-identical tests, truly meaningless names like `Test1` \u002F `test` \u002F `it1`.\n- **Low**: Cosmetic naming mismatches, minor style preferences, assertion messages that could be better. When in doubt, rate Low.\n- **Use the caller's severity vocabulary consistently.** If the caller asks for\n  Critical \u002F Warning \u002F Info, map latent reliability risks to Warning and\n  maintenance\u002Fcosmetic concerns to Info. Keep a demonstrated false-confidence\n  or current order-dependency root cause Critical; do not downgrade it merely to\n  make every requested tier non-empty. Severity describes the demonstrated\n  failure mode, not how much prose a finding receives.\n- **Separate a systemic finding from its instances.** Coverage touching across a\n  facade is one Critical systemic finding whose evidence lists every affected\n  test. All assertion-free instances, including the last facade method, retain\n  the same false-confidence severity. Report `1 finding \u002F 6 affected tests`, not\n  six findings plus a seventh summary finding, and do not downgrade one instance\n  merely to manufacture multiple tiers.\n- **Do not severity-rank ordinary missing cases as anti-patterns.** Adjacent\n  untested branches, exception paths, and boundaries may be useful coverage\n  opportunities, but list them separately from the anti-pattern counts unless a\n  weak existing test specifically creates the gap. They are not Critical merely\n  because the suite has a systemic Critical issue.\n- **Not an issue** (per-language nuance):\n  - Go and Rust **table-driven loops** with sub-tests (`t.Run` \u002F `for case in cases { ... }`) are *idiomatic*, not \"Conditional Test Logic\". Do NOT flag.\n  - pytest **bare `assert`** is the canonical assertion form, not a missing assertion library. Do NOT flag.\n  - Go tests use `if got != want { t.Errorf(...) }` as canonical equality. Do NOT flag as ad-hoc.\n  - Separate tests for distinct boundary conditions (zero vs. negative vs. null). Do NOT flag as duplicates.\n  - Explicit per-test setup instead of `[TestInitialize]` \u002F `beforeEach` (this *improves* isolation).\n  - Tests that are short and clear but could theoretically be consolidated.\n  - Round-trip or serialization equality with non-trivial input. It is valid\n    metamorphic evidence, not a self-comparison; still recommend one independent\n    representation when producer and consumer could share a defect.\n  - A transformation tested only with an already-transformed input. Keep it out\n    of the tautology count, but report the weak oracle when removing the\n    transformation would still pass. Use an input that must change and pin its\n    independently expected output.\n  - Clone value equality. Keep it, and add distinct-reference or mutation-\n    independence evidence when the contract promises a deep copy.\n  - A validator or accessor returning the original value when pass-through is the\n    production contract. Missing invalid-input cases are a coverage gap, not proof\n    that the existing assertion is tautological.\n\nIMPORTANT: If the tests are well-written, say so clearly up front. Do not inflate severity to justify the review. A review that finds zero Critical\u002FHigh issues and only minor Low suggestions is a valid and valuable outcome. Lead with what the tests do well.\n\n### Step 5: Report findings\n\n**Depth bar — a tidy report that is shallower than an unassisted review is a failure.** Before writing, satisfy all five:\n\n1. **Account for every test in scope.** Build the complete method\u002Ffield inventory\n   before summarizing. For a systematic pattern such as coverage touching,\n   enumerate every affected test at least once rather than giving representative\n   examples. A finding table that silently skips tests (or fixtures like an\n   unused `static HttpClient` field) is incomplete. State the number reviewed.\n2. **Verify the production contract before judging the oracle.** Inspect the\n   actual transformation, DTO fields, and promised identity\u002Fclone semantics.\n   Never invent fields or require lossless round-tripping when production is\n   intentionally lossy.\n   For every suspicious equality, write down the independently known oracle\n   before assigning a finding. If the assertion compares a transformed output\n   with non-trivial input, clone state, snapshot, mock verification, or a\n   framework-native assertion context, explain why it can fail before calling it\n   tautological or assertion-free. Conversely, when a transformation test uses\n   an already-normalized input, call out that the input cannot distinguish the\n   real transformation from a no-op and provide a changing input plus exact\n   expected output. For paired producer\u002Fconsumer APIs, retain the round-trip test\n   and add one independent representation oracle rather than replacing valid\n   metamorphic evidence.\n3. **Make every Critical\u002FHigh fix complete and specific.** Give the replacement assertion with the *exact expected value* (the computed discount, the exact CSV line, the full expected object), not a `\u002F\u002F assert something here` placeholder.\n4. **Name obvious adjacent gaps without widening into mutation analysis** —\n   when production code is supplied, note directly related untested throws,\n   null results, boundary values, and round-trip\u002Fculture-sensitivity risks in an\n   **Adjacent coverage gaps** section. Use `test-gap-analysis` for exhaustive\n   branch-by-branch behavioral gaps.\n5. **Keep the report internally consistent.** Summary counts must equal the enumerated findings. Publish a settled conclusion: do all reconsidering before you write, and never leave \"wait, that's wrong\" \u002F \"this should fail but doesn't\" reasoning in the output.\n6. **Make non-findings decisive.** For a clean or mostly clean small suite, name\n   the suspicious constructs you cleared and the framework rule that makes each\n   valid. Do not bury a clean verdict under a generic checklist or speculative\n   improvements.\n\nPresent findings in this structure:\n\n1. **Summary** -- Total issues found, broken down by severity (Critical \u002F High \u002F Medium \u002F Low). If tests are well-written, lead with that assessment.\n2. **Critical and High findings** -- List each with:\n   - The anti-pattern name\n   - The specific location (file, method name, line)\n   - A brief explanation of why it's a problem\n   - A concrete fix (show before\u002Fafter code when helpful)\n3. **Medium and Low findings** -- Summarize in a table unless the user wants full detail\n4. **Positive observations** -- Call out things the tests do well (sealed class, specific exception types, data-driven tests, clear AAA structure, proper use of fakes, good naming). Don't only report negatives.\n\nBefore publishing, assign each finding a stable identity. A grouped row counts\nas one finding regardless of how many methods it lists; separate rows count\nseparately. Recompute the summary from those rows. Keep `affected tests` as a\ndifferent number so a bundled finding cannot create a hidden count mismatch.\n\n### Step 6: Prioritize recommendations\n\nIf there are many findings, recommend which to fix first:\n\n1. **Critical** -- Fix immediately, these tests may be giving false confidence\n2. **High** -- Fix soon, these cause flakiness or maintenance burden\n3. **Medium\u002FLow** -- Fix opportunistically during related edits\n\n## Validation\n\n- [ ] Every test method in scope is accounted for (reviewed count stated; none silently skipped)\n- [ ] Identity and round-trip findings match the production contract and use only real fields\n- [ ] Every finding includes a specific location (not just a general warning)\n- [ ] Every Critical\u002FHigh finding includes a concrete fix with exact expected values\n- [ ] Adjacent untested error paths and boundary values are called out\n- [ ] Summary counts match the enumerated findings\n- [ ] Grouped findings distinguish finding count from affected-test count\n- [ ] Adjacent coverage opportunities are not inflated into Critical anti-pattern findings\n- [ ] Report covers all categories (assertions, isolation, naming, structure)\n- [ ] Positive observations are included alongside problems\n- [ ] Recommendations are prioritized by severity\n\n## Common Pitfalls\n\n| Pitfall | Solution |\n|---------|----------|\n| Reporting style issues as critical | Naming and formatting are Medium\u002FLow, never Critical |\n| Suggesting rewrites instead of targeted fixes | Show minimal diffs -- change the assertion, not the whole test |\n| Flagging intentional design choices | If `Thread.Sleep` \u002F `time.sleep` \u002F `time.Sleep` is in an integration test testing actual timing, that's not an anti-pattern. Consider context. |\n| Inventing false positives on clean code | If tests follow best practices, say so. A review finding \"0 Critical, 0 High, 1 Low\" is perfectly valid. Don't inflate findings to justify the review. |\n| Flagging separate boundary tests as duplicates | Two tests for zero and negative inputs test different edge cases. Only flag as duplicates when 3+ tests have truly identical bodies differing by a single value. |\n| Rating cosmetic issues as Medium | Naming mismatches (e.g., method name says `ArgumentException` but asserts `ArgumentOutOfRangeException`) are Low, not Medium -- the test still works correctly. |\n| Ignoring the test framework | Use the terminology of the framework you loaded from the language extension; don't describe a pytest suite in MSTest terms. |\n| Missing the forest for the trees | If 80% of tests have no assertions, lead with that systemic issue rather than listing every instance |\n| Trading depth for tidiness | A severity table and positive observations do not substitute for coverage of every test, exact expected values in fixes, and the adjacent error-path\u002Fboundary gaps |\n| Contradicting yourself in the report | Reason first, then write one settled verdict per finding — never emit \"wait, that's wrong\" \u002F \"should fail but doesn't\" reconsiderations |\n| Counts that don't add up | The summary's per-severity totals must match the findings you listed |\n","# Test Anti-Pattern Detection\n\nQuick, pragmatic analysis of test code in any supported language for anti-patterns and quality issues that undermine test reliability, maintainability, and diagnostic value.\n\n> **Language-specific guidance**: Read the caller-provided or runtime-listed\n> `test-analysis-extensions` catalog and its matching language file when needed.\n> Do not invoke the reference-only\n> helper. If its files are absent, use the built-in framework rules below.\n\n## When to Use\n\n- User asks to review test quality or find test smells\n- User wants to know why tests are flaky or unreliable\n- User asks \"are my tests good?\" or \"what's wrong with my tests?\"\n- User requests a test audit or test code review\n- User wants diagnostic findings before deciding what to improve\n\n## When Not to Use\n\n- User wants to write new tests from scratch (use `code-testing`)\n- User wants direct implementation fixes rather than a diagnostic review (use the relevant write\u002Fedit skill)\n- User asks to fix swapped `Assert.AreEqual` argument order in MSTest (use `writing-mstest-tests`)\n- User asks to convert MSTest `DynamicData` from `IEnumerable\u003Cobject[]>` to `ValueTuple` (use `writing-mstest-tests`)\n- User wants to run or execute tests (use `run-tests` for .NET)\n- User wants to migrate between test frameworks or versions (use migration skills)\n- User wants raw .NET coverage collection (use `run-tests`), non-.NET coverage collection or analysis (use native tooling), project-wide .NET coverage\u002FCRAP metrics (use `coverage-analysis`), or named-target .NET CRAP (use `crap-score`)\n- User asks whether tests would catch a bug or wants behavioral\u002Fpseudo-mutation gaps (use `test-gap-analysis`)\n- User wants test-mix or happy-vs-error-path classification, standardized tagging, or trait\u002Fcategory distributions (use `test-tagging`)\n- User wants a deep formal test smell audit with academic taxonomy and extended catalog (use `test-smell-detection`)\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Test scope | No | Test files, classes, directory, or project to analyze. Discover from the current workspace when omitted. |\n| Production code | No | The code under test, for context on what tests should verify |\n| Specific concern | No | A focused area like \"flakiness\" or \"naming\" to narrow the review |\n\n## Workflow\n\n### Step 1: Detect language and load extension\n\nResolve the named test path from the current workspace before asking for input.\nWhen no path is supplied, discover test files under the current directory using\nthe repository manifests and conventional test markers. The skill context's\n`Base directory` is documentation storage, not the user's workspace; never\nresolve target files relative to it.\n\nIf one reader says a path is missing but a workspace glob\u002Fsearch finds it,\nnormalize that exact path and retry. Use a shell text reader (`sed`\u002F`cat` on\nUnix, `Get-Content` on PowerShell) only for a confirmed reader availability,\ntransport, or path-normalization failure and only after verifying the canonical\npath remains inside the current workspace. Stop on content-exclusion,\npermission\u002Fpolicy, workspace-boundary, or unknown failures. Audit any discovered\nfile that a permitted reader can access; never ask the user to paste it. If\nevery permitted reader fails, report the exact blocker without bypassing\nsecurity boundaries.\n\nIdentify the language and framework. Read the matching bundled reference when\nneeded, resolving its filename relative to the supplied catalog.\nCheck only that known reference directory, never installation directories.\nIf unavailable, use the catalog below and report the reference limitation.\n\n### Step 2: Gather the test code\n\nInventory the resolved scope before reading bodies. For one file or class, read\nthat scope directly. For a project or suite, discover test files once, batch\nindependent reads where tools allow, and stop when every discovered test and\nclass-level fixture has a ledger disposition.\n\nUse extension discovery markers when loaded; otherwise use the built-in markers\nin this skill (attributes such as `[TestClass]`\u002F`[Fact]`\u002F`[Test]`,\n`test_*.py`, `*.test.*`, `*_test.go`, `*_spec.rb`, `#[test]`,\n`*.Tests.ps1`, `TEST(...)`, and `TEST_CASE(...)`).\n\nDo not read unrelated production code wholesale. Open the production symbol\ncorresponding to every suspicious test needed to decide whether an assertion,\ntransformation, identity contract, or adjacent gap is real. For a systematic\nfacade\u002Fsurface-area pattern, every invoked member is relevant: read the entire\nsmall production type or inspect each invoked member, then map each weak test to\nthe exact observable result, exception, state change, or boundary it should\nverify.\n\n### Step 3: Scan for anti-patterns\n\nCheck each test file against the anti-pattern catalog below. Report findings\ngrouped by severity. Use extension mappings when loaded; otherwise use the\ncross-framework examples in the catalog.\n\nBefore drafting the report, make a private completeness ledger with one row for\nevery test method and every class-level fixture\u002Fresource. Record its oracle (or\nabsence), exception handling, state\u002Ftime dependencies, concurrency safety,\nprecondition\u002Fassertion order, and disposition. Do not publish until every row is\neither attached to a finding or explicitly judged sound. In particular:\n\n- `actual != oldValue` is a weak mutation oracle: it accepts every wrong new\n  value. Require the exact expected value.\n- Include unused or undisposed class-level resources; method-only scans miss\n  fields such as a static `HttpClient`.\n- Treat an unsynchronized static\u002Fglobal collection as both order-coupled and\n  parallel-unsafe when tests read and write it. Also flag dereferencing a\n  nullable result before the assertion intended to prove it non-null.\n- When production code is supplied, note obvious untested contracts adjacent to\n  a finding, but do not perform exhaustive branch or mutation analysis. Route\n  that broader question to `test-gap-analysis`.\n\n#### Critical -- Tests that give false confidence\n\n| Anti-Pattern | What to Look For |\n|---|---|\n| **No assertions** | Test methods that execute code but never assert anything. A passing test without assertions proves nothing. In .NET look for missing `Assert.*`; in pytest a function with no `assert` and no `pytest.raises`; in Jest no `expect(...)`; in JUnit no `assert*`\u002F`assertThat`; in Go a test that never calls `t.Error*`, `t.Fatal*`, or testify; in RSpec a block with no `expect`; in Pester no `Should`. Mock-call verifications (`verify(mock)`, `expect(mock).toHaveBeenCalled`, `Should -Invoke`) are real assertions. |\n| **Missing await on async assertions (JS\u002FTS, .NET, Python, Kotlin, Swift)** | `expect(promise).resolves.toBe(x)` without `await`\u002F`return`, `pytest-asyncio` test with un-awaited coroutine, `async Task` xUnit test calling `Assert.ThrowsAsync` without `await`, Kotest suspending test without `runTest`, Swift Testing async test without `await`. These tests silently pass even when the underlying assertion would have failed. |\n| **Coverage touching** | Test class that methodically calls every public member on a type — often in alphabetical or declaration order — without asserting meaningful outcomes. Each test typically does `var result = sut.MethodName(...)` (or `result = sut.method_name(...)`, `sut.methodName()`, `sut.MethodName(t)`) with no assertion, or only a trivial null\u002FNone\u002Fnil check. The intent is to inflate code-coverage metrics rather than verify behavior. Distinct from a single assertion-free test: the pattern is *systematic* coverage of the surface area with no real verification. |\n| **Self-referential assertion** | The expected value is computed from the same actual value, such as `Assert.AreEqual(dto.Name, dto.Name)`, `Assert.AreEqual(result, result)`, or equivalents. Do not apply this label merely because a valid identity, clone, serialization, or round-trip contract compares output with input: those assertions can fail. Instead check whether the input exercises a transformation and whether independently known representation, field, reference-identity, or invalid-input assertions are missing. |\n| **Swallowed exceptions** | `try { ... } catch { }`, `catch (Exception)` without rethrowing or asserting (.NET); bare `except:` or `except Exception:` with `pass` (Python); `try { ... } catch (e) {}` (JS\u002FTS\u002FJava); `defer recover()` without re-panic and no assertion (Go); `rescue StandardError` with no assertion (Ruby); `Result::unwrap_or(...)` swallowing errors in a test (Rust); empty `catch` block (Kotlin\u002FSwift). |\n| **Assert in catch block only** | `try { Act(); } catch (Exception ex) { Assert.Fail(ex.Message); }` (and equivalents in other languages) -- use `Assert.ThrowsException` \u002F `pytest.raises` \u002F `expect(fn).toThrow` \u002F `assertThrows` \u002F `assert.Error(t, err)` \u002F `#[should_panic]` \u002F `Should -Throw` \u002F `EXPECT_THROW` instead. The test passes when no exception is thrown even if the result is wrong. |\n| **Always-true assertions** | `Assert.IsTrue(true)`, `Assert.AreEqual(x, x)`, `assert True`, `expect(true).toBe(true)`, `assert.True(t, true)`, `assert!(true)`, or conditions that can never fail. |\n| **Commented-out assertions** | Assertions that were disabled but the test still runs, giving the illusion of coverage. |\n\n#### High -- Tests likely to cause pain\n\n| Anti-Pattern | What to Look For |\n|---|---|\n| **Flakiness indicators** | Wall-clock sleeps\u002Fwaits used for synchronization: `Thread.Sleep` \u002F `Task.Delay` (.NET), `time.sleep` (Python), `setTimeout` \u002F `await new Promise(r => setTimeout(...))` (JS\u002FTS), `Thread.sleep` (Java\u002FKotlin), `time.Sleep` (Go), `sleep` (Ruby\u002FBash), `std::thread::sleep` (Rust), `Start-Sleep` (Pester), `std::this_thread::sleep_for` (C++). Wall-clock reads without abstraction: `DateTime.Now`\u002F`UtcNow`, `datetime.now()`\u002F`datetime.utcnow()`, `Date.now()` \u002F `new Date()`, `System.currentTimeMillis()`, `time.Now()`, `Time.now`, `Instant::now()`, `Date()`\u002F`Date.now`, `Get-Date`, `std::chrono::system_clock::now`. Unseeded randomness: `new Random()`, `random.random()`\u002F`random.randint()`, `Math.random()`, `new Random()` (Java\u002FKotlin), `rand.Int()` without seed, `rand` (Ruby), `rand::random()` (Rust). Environment-dependent paths (hard-coded `C:\\...`, `\u002Ftmp\u002F...`, network hosts). |\n| **Test ordering dependency** | Static\u002Fglobal mutable state modified across tests; setup that doesn't fully reset state (`[TestInitialize]`, `setUp`, `beforeEach`, `before(:each)`, `BeforeEach`, `t.Cleanup`); tests that fail when run individually but pass in suite (or vice versa). Examples per language: `static` fields (.NET\u002FJava), module-level globals (Python), top-level `let`\u002F`const` in test file (JS\u002FTS), `var` package globals (Go), class variables (Ruby), `static mut`\u002F`lazy_static!`\u002F`OnceCell` (Rust), `$script:` variables (PowerShell). |\n| **Over-mocking** | More mock setup lines than actual test logic. Verifying exact call sequences on mocks rather than outcomes. Mocking types the test owns. Per language: Moq\u002FNSubstitute\u002FFakeItEasy (.NET), `unittest.mock` \u002F `pytest-mock` (Python), Jest auto-mocks \u002F Sinon (JS\u002FTS), Mockito\u002FPowerMock (Java), gomock\u002Ftestify mock (Go), RSpec mocks\u002Fmocha (Ruby), `mockall` (Rust), MockK (Kotlin), `Mock` cmdlet (Pester), gmock (C++). For a deep mock audit in .NET, use `exp-mock-usage-analysis`. |\n| **Implementation coupling** | Testing private methods via reflection (`MethodInfo.Invoke`, `getattr` in Python, `(thing as any)` in TS, `Field.setAccessible(true)` in Java, `Object#send` in Ruby, internal `pub(crate)` access in Rust). Asserting on internal state instead of observable behavior. Verifying exact method call counts on collaborators instead of business outcomes. |\n| **Broad exception assertions** | `Assert.ThrowsException\u003CException>(...)` (.NET) \u002F `pytest.raises(Exception)` \u002F `expect(fn).toThrow(Error)` without a message matcher \u002F `assertThrows(Exception.class, ...)` (Java) \u002F `assert.Error(t, err)` without checking the kind \u002F `expect { ... }.to raise_error` without class (RSpec) \u002F `#[should_panic]` without `expected = \"...\"` \u002F `Should -Throw` without `-ExpectedMessage` \u002F `EXPECT_ANY_THROW` instead of `EXPECT_THROW(stmt, SpecificType)`. |\n| **Weak transformation oracle** | A normalization, casing, trimming, mapping, or conversion test supplies an input already in the expected form, so a no-op implementation passes even though the assertion may catch other defects. Use an input that must change and assert an independently derived expected value. A producer\u002Fconsumer round trip is useful but does not replace an independent format assertion when both sides could share the same defect. |\n\n#### Medium -- Maintainability and clarity issues\n\n| Anti-Pattern | What to Look For |\n|---|---|\n| **Poor naming** | Test names like `Test1`, `TestMethod`, or `test` that don't describe the scenario or outcome. Use the loaded extension when available; otherwise follow the existing naming convention in the same suite. |\n| **Magic values** | Unexplained numbers or strings in arrange\u002Fassert: `Assert.AreEqual(42, result)` \u002F `assert result == 42` \u002F `expect(result).toBe(42)` -- what does 42 mean? |\n| **Duplicate tests** | Three or more test methods with near-identical bodies that differ only in a single input value. Should be parametrized: `[DataRow]`\u002F`[Theory]`\u002F`[TestCase]` (.NET), `@pytest.mark.parametrize` (pytest), `test.each` \u002F `it.each` (Jest\u002FVitest), `@ParameterizedTest` + `@ValueSource` (JUnit 5), `@DataProvider` (TestNG), Go table-driven tests, `where` \u002F shared examples (RSpec), `#[rstest]` (Rust), `@ParameterizedTest` + `@MethodSource` (Kotlin), `-ForEach` \u002F `-TestCases` (Pester), `INSTANTIATE_TEST_SUITE_P` (GoogleTest), `SECTION` \u002F `GENERATE` (Catch2), `TEST_CASE_TEMPLATE` (doctest). For a detailed duplication analysis in .NET, use `exp-test-maintainability`. Note: Two tests covering distinct boundary conditions (e.g., zero vs. negative) are NOT duplicates -- separate tests for different edge cases provide clearer failure diagnostics and are a valid practice. |\n| **Giant tests** | Test methods exceeding ~30 lines or testing multiple behaviors at once. Hard to diagnose when they fail. |\n| **Assertion messages that repeat the assertion** | `Assert.AreEqual(expected, actual, \"Expected and actual are not equal\")` \u002F `assert x == y, \"x is not equal to y\"` \u002F `assertEquals(x, y, \"values not equal\")` add no information. Messages should describe the business meaning. |\n| **Missing AAA \u002F Given-When-Then separation** | Arrange\u002FAct\u002FAssert (or Given\u002FWhen\u002FThen for BDD frameworks like RSpec, Kotest behavior specs, Pester) phases are interleaved or indistinguishable. |\n\n#### Low -- Style and hygiene\n\n| Anti-Pattern | What to Look For |\n|---|---|\n| **Unused test infrastructure** | Setup\u002Fteardown hooks that do nothing — `[TestInitialize]`\u002F`[SetUp]`\u002F`[BeforeEach]`, `setUp`\u002F`@BeforeEach`\u002F`@BeforeAll`, `beforeEach`\u002F`beforeAll`, `before(:each)`\u002F`before(:all)`, `BeforeEach`\u002F`BeforeAll` (Pester), `setUpWithError` (XCTest) — and test helper methods that are never called. |\n| **Unmanaged resources** | Test creates disposable\u002Fcloseable resources without cleanup: `HttpClient`\u002F`Stream` without `using` (.NET), file\u002Fconnection without `with` block or `try\u002Ffinally` (Python), `FileInputStream` without `try-with-resources` (Java), `defer file.Close()` missing (Go), connection without `ensure` (Ruby), `Drop` not relied on \u002F forgotten `close` (Rust), missing teardown for temp files \u002F DBs in any language. |\n| **Print debugging** | Leftover `Console.WriteLine` \u002F `Debug.WriteLine` \u002F `print()` \u002F `console.log` \u002F `System.out.println` \u002F `fmt.Println` \u002F `puts` \u002F `dbg!` \u002F `Write-Host` \u002F `std::cout` statements used during test development. |\n| **Inconsistent naming convention** | Mix of naming styles in the same test class\u002Fmodule\u002Ffile (e.g., some use `Method_Scenario_Expected`, others use `ShouldDoSomething`). |\n\n### Step 4: Calibrate severity honestly\n\nBefore reporting, re-check each finding against these severity rules:\n\n- **Critical\u002FHigh**: Only for issues that cause tests to give false confidence or be unreliable. A test that always passes regardless of correctness is Critical. Shared mutable state is High when it is a latent isolation risk, but **Critical when the user reports actual order-dependent failures or the code proves one test requires another to run first**. Missing-await on async assertions is Critical (silent pass).\n- **Medium**: Only for issues that actively harm maintainability -- 5+ nearly-identical tests, truly meaningless names like `Test1` \u002F `test` \u002F `it1`.\n- **Low**: Cosmetic naming mismatches, minor style preferences, assertion messages that could be better. When in doubt, rate Low.\n- **Use the caller's severity vocabulary consistently.** If the caller asks for\n  Critical \u002F Warning \u002F Info, map latent reliability risks to Warning and\n  maintenance\u002Fcosmetic concerns to Info. Keep a demonstrated false-confidence\n  or current order-dependency root cause Critical; do not downgrade it merely to\n  make every requested tier non-empty. Severity describes the demonstrated\n  failure mode, not how much prose a finding receives.\n- **Separate a systemic finding from its instances.** Coverage touching across a\n  facade is one Critical systemic finding whose evidence lists every affected\n  test. All assertion-free instances, including the last facade method, retain\n  the same false-confidence severity. Report `1 finding \u002F 6 affected tests`, not\n  six findings plus a seventh summary finding, and do not downgrade one instance\n  merely to manufacture multiple tiers.\n- **Do not severity-rank ordinary missing cases as anti-patterns.** Adjacent\n  untested branches, exception paths, and boundaries may be useful coverage\n  opportunities, but list them separately from the anti-pattern counts unless a\n  weak existing test specifically creates the gap. They are not Critical merely\n  because the suite has a systemic Critical issue.\n- **Not an issue** (per-language nuance):\n  - Go and Rust **table-driven loops** with sub-tests (`t.Run` \u002F `for case in cases { ... }`) are *idiomatic*, not \"Conditional Test Logic\". Do NOT flag.\n  - pytest **bare `assert`** is the canonical assertion form, not a missing assertion library. Do NOT flag.\n  - Go tests use `if got != want { t.Errorf(...) }` as canonical equality. Do NOT flag as ad-hoc.\n  - Separate tests for distinct boundary conditions (zero vs. negative vs. null). Do NOT flag as duplicates.\n  - Explicit per-test setup instead of `[TestInitialize]` \u002F `beforeEach` (this *improves* isolation).\n  - Tests that are short and clear but could theoretically be consolidated.\n  - Round-trip or serialization equality with non-trivial input. It is valid\n    metamorphic evidence, not a self-comparison; still recommend one independent\n    representation when producer and consumer could share a defect.\n  - A transformation tested only with an already-transformed input. Keep it out\n    of the tautology count, but report the weak oracle when removing the\n    transformation would still pass. Use an input that must change and pin its\n    independently expected output.\n  - Clone value equality. Keep it, and add distinct-reference or mutation-\n    independence evidence when the contract promises a deep copy.\n  - A validator or accessor returning the original value when pass-through is the\n    production contract. Missing invalid-input cases are a coverage gap, not proof\n    that the existing assertion is tautological.\n\nIMPORTANT: If the tests are well-written, say so clearly up front. Do not inflate severity to justify the review. A review that finds zero Critical\u002FHigh issues and only minor Low suggestions is a valid and valuable outcome. Lead with what the tests do well.\n\n### Step 5: Report findings\n\n**Depth bar — a tidy report that is shallower than an unassisted review is a failure.** Before writing, satisfy all five:\n\n1. **Account for every test in scope.** Build the complete method\u002Ffield inventory\n   before summarizing. For a systematic pattern such as coverage touching,\n   enumerate every affected test at least once rather than giving representative\n   examples. A finding table that silently skips tests (or fixtures like an\n   unused `static HttpClient` field) is incomplete. State the number reviewed.\n2. **Verify the production contract before judging the oracle.** Inspect the\n   actual transformation, DTO fields, and promised identity\u002Fclone semantics.\n   Never invent fields or require lossless round-tripping when production is\n   intentionally lossy.\n   For every suspicious equality, write down the independently known oracle\n   before assigning a finding. If the assertion compares a transformed output\n   with non-trivial input, clone state, snapshot, mock verification, or a\n   framework-native assertion context, explain why it can fail before calling it\n   tautological or assertion-free. Conversely, when a transformation test uses\n   an already-normalized input, call out that the input cannot distinguish the\n   real transformation from a no-op and provide a changing input plus exact\n   expected output. For paired producer\u002Fconsumer APIs, retain the round-trip test\n   and add one independent representation oracle rather than replacing valid\n   metamorphic evidence.\n3. **Make every Critical\u002FHigh fix complete and specific.** Give the replacement assertion with the *exact expected value* (the computed discount, the exact CSV line, the full expected object), not a `\u002F\u002F assert something here` placeholder.\n4. **Name obvious adjacent gaps without widening into mutation analysis** —\n   when production code is supplied, note directly related untested throws,\n   null results, boundary values, and round-trip\u002Fculture-sensitivity risks in an\n   **Adjacent coverage gaps** section. Use `test-gap-analysis` for exhaustive\n   branch-by-branch behavioral gaps.\n5. **Keep the report internally consistent.** Summary counts must equal the enumerated findings. Publish a settled conclusion: do all reconsidering before you write, and never leave \"wait, that's wrong\" \u002F \"this should fail but doesn't\" reasoning in the output.\n6. **Make non-findings decisive.** For a clean or mostly clean small suite, name\n   the suspicious constructs you cleared and the framework rule that makes each\n   valid. Do not bury a clean verdict under a generic checklist or speculative\n   improvements.\n\nPresent findings in this structure:\n\n1. **Summary** -- Total issues found, broken down by severity (Critical \u002F High \u002F Medium \u002F Low). If tests are well-written, lead with that assessment.\n2. **Critical and High findings** -- List each with:\n   - The anti-pattern name\n   - The specific location (file, method name, line)\n   - A brief explanation of why it's a problem\n   - A concrete fix (show before\u002Fafter code when helpful)\n3. **Medium and Low findings** -- Summarize in a table unless the user wants full detail\n4. **Positive observations** -- Call out things the tests do well (sealed class, specific exception types, data-driven tests, clear AAA structure, proper use of fakes, good naming). Don't only report negatives.\n\nBefore publishing, assign each finding a stable identity. A grouped row counts\nas one finding regardless of how many methods it lists; separate rows count\nseparately. Recompute the summary from those rows. Keep `affected tests` as a\ndifferent number so a bundled finding cannot create a hidden count mismatch.\n\n### Step 6: Prioritize recommendations\n\nIf there are many findings, recommend which to fix first:\n\n1. **Critical** -- Fix immediately, these tests may be giving false confidence\n2. **High** -- Fix soon, these cause flakiness or maintenance burden\n3. **Medium\u002FLow** -- Fix opportunistically during related edits\n\n## Validation\n\n- [ ] Every test method in scope is accounted for (reviewed count stated; none silently skipped)\n- [ ] Identity and round-trip findings match the production contract and use only real fields\n- [ ] Every finding includes a specific location (not just a general warning)\n- [ ] Every Critical\u002FHigh finding includes a concrete fix with exact expected values\n- [ ] Adjacent untested error paths and boundary values are called out\n- [ ] Summary counts match the enumerated findings\n- [ ] Grouped findings distinguish finding count from affected-test count\n- [ ] Adjacent coverage opportunities are not inflated into Critical anti-pattern findings\n- [ ] Report covers all categories (assertions, isolation, naming, structure)\n- [ ] Positive observations are included alongside problems\n- [ ] Recommendations are prioritized by severity\n\n## Common Pitfalls\n\n| Pitfall | Solution |\n|---------|----------|\n| Reporting style issues as critical | Naming and formatting are Medium\u002FLow, never Critical |\n| Suggesting rewrites instead of targeted fixes | Show minimal diffs -- change the assertion, not the whole test |\n| Flagging intentional design choices | If `Thread.Sleep` \u002F `time.sleep` \u002F `time.Sleep` is in an integration test testing actual timing, that's not an anti-pattern. Consider context. |\n| Inventing false positives on clean code | If tests follow best practices, say so. A review finding \"0 Critical, 0 High, 1 Low\" is perfectly valid. Don't inflate findings to justify the review. |\n| Flagging separate boundary tests as duplicates | Two tests for zero and negative inputs test different edge cases. Only flag as duplicates when 3+ tests have truly identical bodies differing by a single value. |\n| Rating cosmetic issues as Medium | Naming mismatches (e.g., method name says `ArgumentException` but asserts `ArgumentOutOfRangeException`) are Low, not Medium -- the test still works correctly. |\n| Ignoring the test framework | Use the terminology of the framework you loaded from the language extension; don't describe a pytest suite in MSTest terms. |\n| Missing the forest for the trees | If 80% of tests have no assertions, lead with that systemic issue rather than listing every instance |\n| Trading depth for tidiness | A severity table and positive observations do not substitute for coverage of every test, exact expected values in fixes, and the adjacent error-path\u002Fboundary gaps |\n| Contradicting yourself in the report | Reason first, then write one settled verdict per finding — never emit \"wait, that's wrong\" \u002F \"should fail but doesn't\" reconsiderations |\n| Counts that don't add up | The summary's per-severity totals must match the findings you listed |\n",{"name":5,"license":15,"description":54},"Audit a test file or suite; produce a severity-ranked diagnostic report. ALWAYS USE for tests that verify nothing, missing\u002Ftautological assertions, swallowed\u002Fbroad exceptions, flaky\u002Forder-dependent tests, duplication, or magic values. Polyglot. DO NOT USE for direct edits: writing-mstest-tests owns supplied MSTest assertions\u002Fattributes\u002Flifecycle; code-testing owns new tests. Exclude running tests, migration, assertion metrics (assertion-quality), raw .NET coverage collection (run-tests), non-.NET coverage collection\u002Fanalysis (native tooling), project-wide .NET coverage\u002FCRAP (coverage-analysis), named-target .NET CRAP (crap-score), behavioral\u002Fpseudo-mutation gaps (test-gap-analysis), test-mix\u002F happy-vs-error classification and trait distributions (test-tagging), or the testsmells.org catalog (test-smell-detection).\n","main","497cc9aba79a931ac6fb8d36a48cc22fe3d6d74b","external-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet-test\u002Fskills\u002Ftest-anti-patterns",41,"https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills","https:\u002F\u002Fskills.managed-code.com","https:\u002F\u002Fgithub.com\u002Fmanagedcode","https:\u002F\u002Fwww.managed-code.com","Open Source Community for .NET Developers: Reliable, Actively Maintained, and Community-Driven. Made in Ukraine 🇺🇦 with ❤️","2026-11-12 19:59:36",{"source":8,"skillName":5,"commands":66,"prompt":76,"zipAvailable":77},[67,70,73],{"tool":68,"command":69},"npx","npx skills add managedcode\u002Fdotnet-skills --skill test-anti-patterns",{"tool":71,"command":72},"bunx","bunx skills add managedcode\u002Fdotnet-skills --skill test-anti-patterns",{"tool":74,"command":75},"pnpm","pnpm dlx skills add managedcode\u002Fdotnet-skills --skill test-anti-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet-test\u002Fskills\u002Ftest-anti-patterns\u002FSKILL.md",true,[79,85,91,95],{"path":80,"resourceType":81,"resourceTypeDesc":82,"sizeBytes":83,"sourceHtmlUrl":84},"core.py","SCRIPT","脚本",360,"https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Farchive\u002Flegacy_pillars\u002F10_engineering_devops\u002F01_software_engineering\u002Fdeveloping\u002Fskills\u002Fcreative_director\u002Fcore.py",{"path":86,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":89,"sourceHtmlUrl":90},"manifest.yaml","REFERENCE","参考文档",182,"https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Farchive\u002Flegacy_pillars\u002F10_engineering_devops\u002F01_software_engineering\u002Fdeveloping\u002Fskills\u002Fcreative_director\u002Fmanifest.yaml",{"path":92,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":93,"sourceHtmlUrl":94},"SKILL.md.meta.json",821,"https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Farchive\u002Flegacy_pillars\u002F10_engineering_devops\u002F01_software_engineering\u002Fdeveloping\u002Fskills\u002Fcreative_director\u002FSKILL.md.meta.json",{"path":96,"resourceType":81,"resourceTypeDesc":82,"sizeBytes":97,"sourceHtmlUrl":98},"tests\u002Ftest_core.py",302,"https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Farchive\u002Flegacy_pillars\u002F10_engineering_devops\u002F01_software_engineering\u002Fdeveloping\u002Fskills\u002Fcreative_director\u002Ftests\u002Ftest_core.py",[100,133,156,188,198,225],{"skillNo":101,"name":102,"nameZh":6,"description":103,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":114,"publishedAt":49},"01M6DYQE1Y9FHAQHEDTVFQ3447","xtwitter-scraper","Use XTwitterScraper when a .NET application needs typed access to Xquik's X automation REST API for tweet search, user lookup, posting workflows, extraction jobs, monitors, webhooks, or giveaway draws. USE FOR: integrating the XTwitterScraper NuGet package; reviewing Xquik API client usage; modeling API-key configuration, pagination, binary exports, raw responses, retries, and webhook\u002Fevent flows in .NET. DO NOT USE FOR: unrelated stacks; direct browser automation; generic social-media strategy; or code that does not call Xquik. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fcatalog\u002FLibraries\u002FXTwitterScraper\u002Fskills\u002Fxtwitter-scraper\u002FSKILL.md",[106,109],{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":110,"categoryName":111,"taxonomyType":112,"taxonomyTypeDesc":113},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[115,116,117,47,46,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132],"xtwitter scraper","browser automation","workflows","browser","search","repository","api","Media","api client","access","edit","unrelated","webhooks","files","social","commands","modeling","webhook",{"skillNo":134,"name":135,"nameZh":6,"description":136,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":137,"categories":138,"keywords":145,"publishedAt":49},"01M6DYQ9S4PE1H6G8MCH9T21HR","sep","Use Sep for high-performance separated-value parsing and writing in .NET, including delimiter inference, explicit parser\u002Fwriter options, and low-allocation row\u002Fcolumn workflows. USE FOR: delimited data needs are performance-sensitive and allocation-aware; project needs explicit control over separator inference, escaping, trimming, and header behavior;. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fcatalog\u002FLibraries\u002FSep\u002Fskills\u002Fsep\u002FSKILL.md",[139,142],{"categoryNo":140,"categoryName":141,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":143,"categoryName":144,"taxonomyType":112,"taxonomyTypeDesc":113},"01JBSEEDCAT000000000000036","内容创作者",[135,48,146,117,147,47,120,148,149,150,125,151,126,128,152,153,154,36,130,155],"performance","tasks","data","parsing","parser","context","do","writer","options","high-performance",{"skillNo":157,"name":158,"nameZh":6,"description":159,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":160,"categories":161,"keywords":162,"publishedAt":49},"01M6DZS2DNEPXCGGF6SSCW6G5V","webgpu-threejs-tsl","Comprehensive guide for developing WebGPU-enabled Three.js applications using TSL (Three.js Shading Language). Covers WebGPU renderer setup, TSL syntax and node materials, compute shaders, post-processing effects, and WGSL integration. Use this skill when working with Three.js WebGPU, TSL shaders, node materials, or GPU compute in Three.js.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fwebgpu-claude-skill\u002Fskills\u002Fwebgpu-threejs-tsl\u002FSKILL.md",[],[163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187],"webgpu","threejs","webgpu threejs tsl","setup","shaders","gpu","integration","applications","js","node","syntax","guide","use","language","three","compute","post","processing","with","renderer","for","materials","wgsl","effects","when",{"skillNo":189,"name":190,"nameZh":6,"description":191,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":192,"categories":193,"keywords":194,"publishedAt":49},"01M6DZS0BQXW2B56A289XF1X76","roslynk","Use Roslynk for semantic inspection and edits of *.cs, *.razor and *.cshtml solution files.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Froslynk\u002Fskills\u002Froslynk\u002FSKILL.md",[],[190,175,128,195,196,183,197],"semantic","razor","inspection",{"skillNo":199,"name":200,"nameZh":6,"description":201,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":202,"categories":203,"keywords":204,"publishedAt":49},"01M6DZRW6MEJCVW80K3S4XV60K","system-text-json-net11","Imperative guidance for the System.Text.Json APIs added in .NET 11: the built-in `JsonNamingPolicy.PascalCase` naming policy, and the strongly-typed generic `JsonSerializerOptions.GetTypeInfo` and `JsonSerializerOptions.TryGetTypeInfo` metadata accessors (generic overloads that return a typed `JsonTypeInfo`). USE ONLY when the user is targeting net11.0 or later and needs PascalCase JSON property or dictionary-key names without writing a custom naming policy, a strongly-typed generic `JsonTypeInfo` instead of the non-generic `JsonTypeInfo`, or a no-throw way to probe whether a type's serialization metadata is resolved. DO NOT USE when the target is earlier than net11.0, the requested behavior uses an established pre-net11 naming policy, or the user explicitly selected another JSON library.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet11\u002Fskills\u002Fsystem-text-json-net11\u002FSKILL.md",[],[205,206,207,208,48,209,210,211,212,213,214,175,215,216,217,218,152,219,220,221,222,183,223,224,187],"system","json","text","system text json net11","policy","metadata","library","serialization","behavior","user","only","probe","guidance","later","type","dictionary","naming","targeting","custom","generic",{"skillNo":226,"name":227,"nameZh":6,"description":228,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":229,"categories":230,"keywords":233,"publishedAt":49},"01M6DZRRJP3S662NFSG1RSPQQ7","process-api-net11","Provides guidance on the new System.Diagnostics.Process APIs introduced in .NET 11: Process.Run, Process.RunAndCaptureText, Process.StartAndForget, Process.ReadAllText\u002FBytes\u002FLines, KillOnParentExit, InheritedHandles, and StartDetached. USE FOR: starting, orchestrating, or capturing output from external processes in applications targeting net11.0 or later. DO NOT USE FOR: applications targeting .NET 10 or earlier, or basic pre-.NET 11 Process.Start usage without new APIs.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet11\u002Fskills\u002Fprocess-api-net11\u002FSKILL.md",[231,232],{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":110,"categoryName":111,"taxonomyType":112,"taxonomyTypeDesc":113},[121,234,235,236,47,237,170,205,175,217,218,152,238,239,240,241,222,183,29],"process","process api net11","diagnostics","start","basic","processes","output","usage"]