agentsclimarketplace

Test quality

Skill rollinsio/beyond-test-coverage/.claude/skills/test-quality

Audit, harden, or generate unit tests with a focus on mutation-resistance and durable quality — not just coverage %. Use when the user asks to improve or harden test quality, make tests less fragile/brittle, review tests for anti-patterns (error-message-substring asserts, private-symbol access, tautological constructor readbacks, recomputed-crypto expectations, over-mocking, missing boundary tests, unrolled cases that should be parametrized), generate a high-quality suite for an untested module, or generally "make my tests better". Measures a suite against a validated quality scorecard and iterates until it plateaus, holding coverage as a non-regression floor and REPL-verifying library assumptions. Works across stacks — Python/pytest (validated), JavaScript/TypeScript (Jest, Vitest, Mocha/Chai, node:test), Go, Kotlin (kotlin.test/JUnit5/Kotest), and Swift (XCTest/Swift Testing/Quick) — with a per-language scorer; the rubric is the same everywhere.From its SKILL.md

Install
npx -y skills add rollinsio/beyond-test-coverage --skill test-quality

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

9.1 KB, as published. Nobody here has run it

test-quality

Bring a target's tests up to a durable-quality bar. "Quality" means tests that fail when behavior breaks and survive when it's only refactored — the opposite of coverage-chasing suites that hit 100% yet catch nothing.

This skill encodes the result of a controlled experiment: pointing test generation at a multi-axis quality scorecard (rather than a coverage number) produced suites that beat human-written baselines on the auto-countable axes in 9 of 9 Python suites (8 of 9 with the model held fixed — the rubric, not the model, drove the gain). The two reference docs are that experiment's distilled output:

  • references/quality-contract.md — 10 anti-fragility rules, each with the repair.
  • references/scorecard.md — the scoring axes, the improvement gate, the stop condition.
  • scripts/score.py — measures the auto-countable axes for a suite in any supported language (Python/pytest, JS/TS, Go, Kotlin, Swift).

Read both reference docs before starting. They are the substance; this file is the procedure.

Core principle

Coverage is a floor, not a goal. Once the suite is at or above its starting coverage, more coverage doesn't count as improvement. What counts is moving the quality axes: fewer fragility patterns, more rigor signals, less real mocking, better LOC efficiency — with every test traceable to a user-observable contract.

Inputs

  • Target (required): a module/package to test, an existing test file/dir to improve, or both. If the user didn't specify, ask what to target.
  • Test command (auto-detect, confirm if unsure): how to run the suite with coverage. Detect from the project's config/manifest, then by stack:
    • pytest: python -m pytest <tests> --cov=<src> --cov-branch
    • Jest: npx jest --coverage · Vitest: npx vitest run --coverage
    • Mocha: npx c8 --check-coverage mocha (c8/nyc for coverage)
    • Go: go test -cover -coverprofile=cover.out ./... && go tool cover -func=cover.out
    • Kotlin: ./gradlew test (coverage via the JaCoCo/Kover plugin if configured)
    • Swift: swift test --enable-code-coverage (SwiftPM) or xcodebuild test (Xcode) Read pyproject.toml/pytest.ini, package.json(scripts.test, jest/vitest config), go.mod, build.gradle(.kts)/settings.gradle, Package.swift, Makefile to find the project's real command — prefer it over the defaults above.

Procedure

1. Scope & detect

Identify the source package and its tests dir. Detect the language/framework (this sets the score.py --lang profile: python, js, go, kotlin, or swift) and the coverage-enabled test command. Determine the mode:

  • tests exist for the target → audit + improve (refactor in place).
  • target is untested → generate a fresh suite. (A target can be mixed: improve what exists, generate for the gaps.)

2. Capture the baseline (do this BEFORE changing anything)

  • Run the suite with branch coverage; record line % and branch % — this is the coverage floor.
  • python <skill>/scripts/score.py --tests <tests_dir> [--lang python|js|go|kotlin|swift] for the auto axes (--lang auto-detects if omitted).
  • Preserve the starting state so you can score against it: copy the tests dir aside, or note the git ref. Later runs use --baseline <that copy>.
  • For generate mode with no existing tests, the floor is the source's current coverage (often 0) and the baseline scorecard is empty — the bar is then the contract itself (zero violations), not "beat the old suite".

3. Audit against the contract

Read the tests (or, for generate mode, the source) and inventory:

  • The auto axes from score.py.
  • The judgement axes by reading: A.3 tautological readbacks, A.6 hand-coded char-sets, B.2 boundary coverage (enumerate every <,<=,>,>=,==,!= in the source and check each has a boundary test), B.3 real-I/O fixture use, E.3 contract-naming. Produce a concrete findings list: file:line → which rule → the repair.

4. Improve — iterate to plateau

Loop. Each round, make ONE focused, justified move:

  • Refactor an existing fragile test (apply the contract repair in place — do not leave the old version; rewrite it), or
  • Add a missing rigor test (a boundary case, a fixed-vector pin, a real-I/O/integration test, a parametrized consolidation), or
  • Generate a cohesive new test file for an untested unit.

Rules for the loop:

  • REPL-verify first. Before asserting on any stdlib/library runtime behavior, run it and assert what actually happens — python -c "...", node -e "..." (or a scratch test you delete), go run a snippet. Don't assert from memory. Keep a short log of what you checked.
  • Public API only; pin fixed expected literals; cover the boundary; prefer real-I/O fixtures over unittest.mock; parametrize instead of unrolling.
  • Run the suite after each move. All tests must pass before you keep a move.
  • Apply the gate (references/scorecard.md): keep the move only if the coverage floor holds, an A-axis dropped or a B-axis rose, nothing regressed, and every new test maps to a stated contract. If a move fails the gate, revert it — a no-op round is not progress.
  • Re-score after each round (score.py --tests <tests> --baseline <start>).

Stop when 3 consecutive rounds can't produce a gated improvement, OR there are no contract violations left and every source boundary has a test.

5. Verify & report

  • Final full run: suite passes, coverage ≥ floor on both line and branch.
  • Print the before/after scorecard (auto axes as a W/L/T tally vs. the captured baseline) plus the judgement-axis assessment.
  • List residual risks honestly: boundaries still uncovered, mutations likely to survive, anything you couldn't verify.

Hard rules

  • Never weaken a test to win an axis. Deleting a strict assertion to drop an A-count, or loosening == to in, is a regression even if the score "improves". The gate's rule 3 exists to catch exactly this.
  • Never chase coverage through private internals. If you can't name the user-observable contract a test protects, don't write it.
  • Don't invent library behavior. REPL-verify or read the docs (check context7 for library docs) — never assert from memory about what a call raises/returns.
  • Match the existing suite's style, fixtures, and naming when improving in place.

Notes

  • Validation tiers. Python/pytest is empirically validated (the scorer's numbers were checked against a controlled experiment). The js (Jest/Vitest/ Mocha/node:test), go, kotlin (kotlin.test/JUnit5/Kotest), and swift (XCTest/Swift Testing/Quick) profiles apply the same axes with heuristic regexes — trustworthy for trends and worst-offenders, but lean harder on reading the tests, and treat the W/L/T tally as indicative, not authoritative. The kotlin/swift regexes were calibrated against six real, well-tested suites (see tests/test_score.py for the named regressions). Two language-specific notes: Kotest spec leaves are only counted when written name(...) { … } with a body (StringSpec's "name" { … } and BehaviorSpec given/when/then are missed), and A.2 (private access) is n/a for Swift — @testable import of internal is idiomatic and private is unreachable (as same-package access is in Go).
  • The counts are plain greps. Matches inside comments and string literals count (both toward the axes and toward test_count), and the A/B count axes are absolute numbers that scale with suite size — so never optimize the numbers themselves; the gate (references/scorecard.md) and reading the tests are what keep the score honest. Known counting gaps are pinned as xfail in tests/test_score.py.
  • Other stacks (RSpec, xUnit, JUnit-for-Java, …): the rubric and contract still apply by judgment. To add a first-class profile, extend PROFILES in scripts/score.py (file globs + a test_def regex + the per-axis regexes) — it's a self-contained dict per language.
  • For a large target, scope to one module/package per invocation rather than a whole repo at once; the iterate-to-plateau loop is per-target.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.