Testing Testing v0.2.18

Test Trait Tagging

Classifies existing tests by standard traits and reports their distribution. MUST USE to categorize/tag/label tests, compare happy vs error paths, audit the test mix, or describe coverage shape by test type. Read bodies when names mislead. Apply canonical attributes; otherwise report only. DO NOT USE for test-quality audits, executed coverage or CRAP, behavioral gaps, writing tests, or migration.

Workflow

Step 1: Detect the language, framework, and tagging capability

Identify the codebase's language and test framework. Call the test-analysis-extensions skill and read the matching extension file. The extension file declares a tag-support capability for each framework:

  • `auto-edit` — framework has canonical tag syntax this skill can safely insert (.NET [TestCategory] / [Trait] / [Category] / [Property], pytest @pytest.mark.<name>, JUnit 5 @Tag("..."), TestNG groups = {"..."}, RSpec metadata it "..." , :tag => true, Pester -Tag '...', Kotest @Tags(...), Swift Testing @Tag(.tagName), Catch2 [tag], doctest * doctest::test_suite("tag") decorator).
  • `report-only` — framework has no canonical, agreed-upon tag attribute; report tags in a Markdown table only and do not edit source (Go standard testing without build-tag conventions, Jest/Vitest without consistent describe-prefix convention, Rust without project-specific cfg conventions, XCTest without a test plan, GoogleTest without test-name prefix conventions, Mocha without describe-prefix conventions).
  • `convention-based` — framework uses naming or file conventions for tagging (Go //go:build integration build tags, file-name suffixes like *_integration_test.go, GoogleTest INTEGRATION_* filter prefix). Only emit canonical edits when the user has confirmed the project convention; otherwise treat as report-only.

Capture the capability before Step 4.

Step 2: Scan existing traits

Check which tests already have trait attributes. Use the loaded language extension as the source of truth — examples:

| Framework | Existing Attribute | Example | |-----------|--------------------|---------| | MSTest | [TestCategory("...")] | [TestCategory("positive")] | | xUnit | [Trait("Category", "...")] | [Trait("Category", "positive")] | | NUnit | [Category("...")] | [Category("positive")] | | TUnit | [Property("Category", "...")] | [Property("Category", "positive")] | | JUnit 5 | @Tag("...") | @Tag("positive") | | TestNG | @Test(groups = {"..."}) | @Test(groups = {"positive"}) | | pytest | @pytest.mark.<name> | @pytest.mark.positive | | RSpec | metadata after it | it "...", :positive do | | Pester | -Tag '...' | It '...' -Tag 'positive' | | Kotest | @Tags(...) | @Tags(Positive) | | Swift Testing | @Tag(.<name>) | @Test(.tags(.positive)) | | Catch2 | [tag] in name | TEST_CASE("...", "[positive]") | | doctest | * doctest::test_suite("...") decorator | TEST_CASE("..." *doctest::test_suite("positive")) |

Record which tests already have tags to avoid duplication.

Step 3: Classify each test method

Build one canonical inventory containing each discovered test exactly once. Record the test identifier, behavioral classification, and traits in that inventory; use the same rows for source edits, per-test reporting, totals, and distribution counts. Do not hand-count a separate denominator. Before publishing, reconcile the reported total with the number of inventory rows and verify that every row contributes to each displayed trait count.

For each test method without traits, analyze:

  1. Method name -- names containing Invalid, Fail, Error, Throw, Reject, BadInput, Null, None, Nil, Negative, raises_, _throws_, _returns_error suggest negative
  2. Assertion type -- Assert.ThrowsException / Assert.Throws / Should().Throw() / pytest.raises / expect(fn).toThrow / assertThrows / assert.Error(t, err) / expect { ... }.to raise_error / #[should_panic] / XCTAssertThrowsError / Should -Throw / EXPECT_THROW suggest negative
  3. Input values -- null / None / nil / undefined, "", 0, -1, int.MaxValue / sys.maxsize / Number.MAX_SAFE_INTEGER / math.MaxInt64 / i32::MAX, empty collections suggest boundary
  4. Setup complexity -- minimal setup with basic assertions suggests smoke; external dependencies (file/db/net/env) suggest integration
  5. Comments and names -- references to issue numbers or "regression" / "bug" / "fix for #..." suggest regression
  6. Timing assertions -- Stopwatch, BenchmarkDotNet, elapsed-time checks; pytest-benchmark fixtures; benchmark.js; JMH @Benchmark; go test -bench; criterion.rs; XCTMetric; Google Benchmark; kotlinx-benchmark suggest performance
  7. Feature centrality -- tests on primary public API entry points or critical user workflows suggest critical-path
  8. Security patterns -- validates auth, checks permissions, sanitizes input, tests for injection, handles tokens/secrets suggest security
  9. Parallel/async constructs -- per-language concurrency primitives (see Trait Taxonomy table) suggest concurrency
  10. Fault injection -- simulates failures, tests retries, timeouts, or circuit breakers suggest resilience
  11. State mutation -- deletes external records, drops resources, modifies shared/global state suggest destructive
  12. Full-stack flow -- test spans entry point through data layer to final response, covering a complete user scenario suggest end-to-end
  13. Config/settings -- loads configuration, tests missing keys, validates options, checks environment variables suggest configuration
  14. Known instability -- test has skip / ignore annotations with comments about flakiness, or names contain "flaky" / "intermittent" suggest flaky
  15. 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 (or report only)

If the loaded language extension declares `auto-edit` for the framework, add the appropriate attribute to each test method. Place trait attributes adjacent to the existing test attribute. Examples:

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
{ ... }

pytest:

@pytest.mark.negative
@pytest.mark.boundary
def test_parse_none_input_raises_value_error():
    ...

JUnit 5:

@Test
@Tag("positive")
@Tag("critical-path")
void createOrder_validItems_returnsConfirmation() { ... }

TestNG:

@Test(groups = {"negative", "boundary"})
public void parse_nullInput_throwsIllegalArgumentException() { ... }

RSpec:

it "rejects null input", :negative, :boundary do
  ...
end

Pester:

It 'Rejects null input' -Tag 'negative','boundary' {
    ...
}

Kotest:

@Tags(Negative, Boundary)
class ParserSpec : StringSpec({
    "rejects null input" { ... }
})

Swift Testing:

@Test(.tags(.negative, .boundary))
func parseNullInputThrows() throws { ... }

Catch2:

TEST_CASE("Parse null input throws", "[negative][boundary]") { ... }

If the loaded language extension declares `report-only` for the framework (Go standard testing, plain Jest/Vitest without convention, Rust without project-specific cfg, plain XCTest, plain GoogleTest, plain Mocha), do NOT modify source files. Instead emit a concise mapping from each test to its suggested tags. Recommend a project-wide convention only when the user asks how to persist or filter those tags; an analysis-only request should report and stop.

If the loaded language extension declares `convention-based` (e.g., Go //go:build integration, *_integration_test.go, GoogleTest INTEGRATION_* prefix), only emit canonical edits when the user has confirmed the project's convention. Otherwise treat as report-only.

Step 5: Generate trait summary

After tagging, produce a summary table. Include only traits with a non-zero count unless the user asks for the full taxonomy; zero-filled rows obscure the suite's actual shape. For a small report-only suite, keep the per-test mapping and non-zero distribution together rather than expanding into a dashboard.

Related skills

Testing Testing 1,167 tokens

Use the open-source free `coverlet` toolchain for .NET code coverage.

coverlet.collectorcoverlet.msbuild
Testing Testing 1,199 tokens

Write, run, or repair .NET tests that use MSTest.

MSTestMSTest.TestFrameworkMSTest.TestAdapter
Testing Testing 2,524 tokens

Write, run, or repair .NET tests that use NUnit.

NUnitNUnit3TestAdapter