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