{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/test-tagging",
  "version": "1.0.0",
  "name": "test-tagging",
  "description": "Analyzes test suites and tags each test with a standardized set of traits (e.g., positive, negative, critical-path, boundary, smoke, regression). Use when the user wants to categorize, audit, or label tests with traits. Do not use for writing new tests, running tests, or migrating test frameworks.",
  "system_prompt_fragment": "# Test Trait Tagging\n\nAnalyze an existing test suite and apply a standardized set of trait tags to each test method, giving teams visibility into their test distribution (positive vs. negative, critical-path coverage, smoke tests, etc.).\n\n## When to Use\n\n- Auditing a test project to understand the mix of test types\n- Adding trait attributes to untagged tests\n- Generating a summary report of trait distribution across a test suite\n- Reviewing whether critical paths have sufficient coverage\n\n## When Not to Use\n\n- Writing new tests from scratch (use `writing-mstest-tests`)\n- Running or filtering tests (use `run-tests`)\n- Migrating between test frameworks\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Test project or files | Yes | Path to the test project, folder, or specific test files to analyze |\n| Scope | No | `tag` (apply attributes), `audit` (report only), or `both` (default: `both`) |\n| Framework | No | Auto-detected. Override with `mstest`, `xunit`, or `nunit` if detection fails |\n\n## Trait Taxonomy\n\nUse exactly these trait names and values. Do not invent new trait values outside this table.\n\n| Trait Value | Meaning | Heuristics |\n|-------------|---------|------------|\n| `positive` | Verifies expected behavior under normal/valid conditions | Asserts success, valid output, expected state, no exceptions for valid input |\n| `negative` | Verifies correct handling of invalid input, errors, or edge cases | Asserts exceptions, error codes, validation failures, rejects bad input |\n| `boundary` | Tests limits, thresholds, empty/null inputs, min/max values | Operates on `0`, `-1`, `int.MaxValue`, empty string, null, empty collection, boundary of valid range |\n| `critical-path` | Core workflow that must never break; breakage blocks users | Tests the primary success scenario of a key public API or user-facing feature |\n| `smoke` | Quick sanity check that the system is operational | Fast, no complex setup, verifies basic wiring (e.g., service resolves, endpoint returns 200) |\n| `regression` | Reproduces a specific previously-reported bug | References a bug ID, issue number, or describes a fix in its name or comments |\n| `integration` | Crosses process, network, or persistence boundaries | Uses real database, HTTP client, file system, external service, or multi-component setup |\n| `end-to-end` | Full user workflow spanning the entire application stack | Exercises a complete scenario from entry point to final result, distinct from single-boundary `integration` |\n| `performance` | Validates timing, throughput, or resource consumption | Asserts on elapsed time, memory, allocations, or uses benchmark harness |\n| `security` | Verifies authentication, authorization, input sanitization, or secrets handling | Tests for SQL injection, XSS, CSRF, unauthorized access, token validation, permission checks |\n| `concurrency` | Validates thread safety, parallelism, or async correctness | Uses `Task.WhenAll`, locks, `Parallel.ForEach`, `SemaphoreSlim`, reproduces race conditions |\n| `resilience` | Tests retry logic, timeouts, circuit breakers, or graceful degradation | Asserts behavior under transient failures, network drops, or service unavailability (e.g., Polly policies) |\n| `destructive` | Mutates shared or external state that is hard to roll back | Deletes records, drops resources, modifies global config -- useful for CI isolation decisions |\n| `configuration` | Verifies settings loading, defaults, environment behavior | Tests missing config keys, invalid values, environment variable fallbacks, options validation |\n| `flaky` | Known to intermittently fail (meta-tag for test health tracking) | Mark tests the team knows are unreliable; used to quarantine or prioritize stabilization |\n\nA single test may have **multiple traits** (e.g., both `negative` and `boundary`). At minimum, every test should receive one of `positive` or `negative`.\n\n## Workflow\n\n### Step 1: Detect the test framework\n\nExamine project files and source code to determine the framework — see the `dotnet-test-frameworks` skill for the complete detection table (package references, test markers, assertion APIs, and skip annotations).\n\n### Step 2: Scan existing traits\n\nCheck which tests already have trait attributes:\n\n| Framework | Existing Attribute | Example |\n|-----------|--------------------|---------|\n| MSTest | `[TestCategory(\"...\")]` | `[TestCategory(\"positive\")]` |\n| xUnit | `[Trait(\"Category\", \"...\")]` | `[Trait(\"Category\", \"positive\")]` |\n| NUnit | `[Category(\"...\")]` | `[Category(\"positive\")]` |\n\nRecord which tests already have tags to avoid duplication.\n\n### Step 3: Classify each test method\n\nFor each test method without traits, analyze:\n\n1. **Method name** -- names containing `Invalid`, `Fail`, `Error`, `Throw`, `Reject`, `BadInput`, `Null`, `Negative` suggest `negative`\n2. **Assertion type** -- `Assert.ThrowsException`, `Assert.Throws`, `Should().Throw()` suggest `negative`\n3. **Input values** -- `null`, `\"\"`, `0`, `-1`, `int.MaxValue`, `int.MinValue`, empty collections suggest `boundary`\n4. **Setup complexity** -- minimal setup with basic assertions suggests `smoke`; external dependencies suggest `integration`\n5. **Comments and names** -- references to issue numbers or \"regression\" / \"bug\" / \"fix for #...\" suggest `regression`\n6. **Timing assertions** -- `Stopwatch`, `BenchmarkDotNet`, elapsed-time checks suggest `performance`\n7. **Feature centrality** -- tests on primary public API entry points or critical user workflows suggest `critical-path`\n8. **Security patterns** -- validates auth, checks permissions, sanitizes input, tests for injection, handles tokens/secrets suggest `security`\n9. **Parallel/async constructs** -- `Task.WhenAll`, `Parallel.ForEach`, locks, `SemaphoreSlim`, `ConcurrentDictionary`, race condition names suggest `concurrency`\n10. **Fault injection** -- simulates failures, tests retries, timeouts, or circuit breakers suggest `resilience`\n11. **State mutation** -- deletes external records, drops resources, modifies shared/global state suggest `destructive`\n12. **Full-stack flow** -- test spans entry point through data layer to final response, covering a complete user scenario suggest `end-to-end`\n13. **Config/settings** -- loads configuration, tests missing keys, validates options, checks environment variables suggest `configuration`\n14. **Known instability** -- test has `[Ignore]`/`[Skip]` comments about flakiness, or names contain \"flaky\"/\"intermittent\" suggest `flaky`\n15. **Default** -- if the test verifies a normal success path, tag `positive`\n\nWhen in doubt between `positive` and `negative`, read the assertion: if it asserts success -> `positive`; if it asserts failure -> `negative`.\n\n### Step 4: Apply trait attributes\n\nAdd the appropriate attribute to each test method. Place trait attributes on the line directly above or below the existing test attribute.\n\n**MSTest:**\n```csharp\n[TestMethod]\n[TestCategory(\"negative\")]\n[TestCategory(\"boundary\")]\npublic void Parse_NullInput_ThrowsArgumentNullException() { ... }\n```\n\n**xUnit:**\n```csharp\n[Fact]\n[Trait(\"Category\", \"positive\")]\n[Trait(\"Category\", \"critical-path\")]\npublic void CreateOrder_ValidItems_ReturnsConfirmation() { ... }\n```\n\n**NUnit:**\n```csharp\n[Test]\n[Category(\"regression\")]\n[Category(\"negative\")]\npublic void Calculate_OverflowInput_ReturnsError() // Fix for #1234\n{ ... }\n```\n\n### Step 5: Generate trait summary\n\nAfter tagging, produce a summary table:\n\n```\n## Trait Distribution\n\n| Trait         | Count | % of Total |\n|---------------|-------|------------|\n| positive      |    42 |      53.8% |\n| negative      |    22 |      28.2% |\n| boundary      |     8 |      10.3% |\n| critical-path |    12 |      15.4% |\n| smoke         |     3 |       3.8% |\n| regression    |     5 |       6.4% |\n| integration   |     4 |       5.1% |\n| end-to-end    |     2 |       2.6% |\n| performance   |     1 |       1.3% |\n| security      |     3 |       3.8% |\n| concurrency   |     2 |       2.6% |\n| resilience    |     1 |       1.3% |\n| destructive   |     1 |       1.3% |\n| configuration |     2 |       2.6% |\n| flaky         |     1 |       1.3% |\n| **Total tests** | **78** | -- |\n\nNote: Percentages exceed 100% because tests can have multiple traits.\n```\n\nInclude observations such as:\n- Ratio of positive to negative tests\n- Whether critical-path tests exist for key public APIs\n- Any tests that could not be confidently classified (list them for manual review)\n\n## Validation\n\n- [ ] Every test method has at least one trait attribute (`positive` or `negative` at minimum)\n- [ ] No invented trait values outside the taxonomy table\n- [ ] Existing trait attributes were preserved, not duplicated\n- [ ] The trait summary table was generated\n- [ ] The project still builds after changes (`dotnet build`)\n\n## Common Pitfalls\n\n| Pitfall | Solution |\n|---------|----------|\n| Guessing traits without reading the test body | Always read assertions and setup to classify accurately |\n| Tagging a test only as `boundary` without `positive`/`negative` | Every test should also be `positive` or `negative` -- `boundary` is additive |\n| Using `TestCategory` syntax in an xUnit project | Match the attribute style to the detected framework |\n| Duplicating an existing category attribute | Check for pre-existing traits in Step 2 before adding |\n| Over-tagging as `critical-path` | Reserve for tests on primary public entry points, not every helper |",
  "applicable_domains": [
    "code",
    "dotnet",
    "engineering"
  ],
  "invocation": [
    "/test-tagging"
  ],
  "tags": [
    "dotnet-test",
    "dotnet",
    "csharp",
    "microsoft"
  ],
  "authored_by": "anthropics",
  "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-test/skills/test-tagging/SKILL.md",
  "lifecycle": "stable",
  "category": "dotnet",
  "provenance": {
    "source": "dotnet/skills",
    "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-test/skills/test-tagging/SKILL.md",
    "author": "Microsoft / .NET Foundation",
    "license": "MIT",
    "notes": "Imported by scripts/import-anthropic-skills.py."
  }
}