tdd — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited tdd (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
TDD uses tests as a design tool. The test suite is a byproduct — the real output is clean design that emerges from writing tests first. Code is only written to satisfy a failing test.
Gather context before writing anything. A wrong assumption means wrong tests.
about tooling if nothing exists yet.
inventing them.
ask for a concrete scenario: "What should happen when a user logs in with the wrong password?" One focused question beats a checklist.
Every unit of work follows three steps. Never skip the third.
rejects_login_with_wrong_password, not test_login_2
exact value (e.g., a specific error message string), assert the contract you care about (e.g., "raises ValueError") rather than guess. Tweaking the assertion later to match whatever the implementation happens to emit is test-after, not test-first — see Pitfalls.
clarity, not deduplication (see DAMP below)
Repeat for each new behavior.
Before writing any code, list the scenarios:
Work simplest to most complex. Each test should force a small, specific generalization in the production code. As tests get more specific, the code gets more generic — if logically equivalent cases wouldn't pass without changes, the production code is still too specific.
For bug fixes: write a test that reproduces the bug first (it fails), fix it (it passes), refactor. The test is regression protection forever.
Arrange (Given): set up the object/state under test
Act (When): call the behavior being tested
Assert (Then): verify the result matches expectationOne behavior per test. Multiple assertions are fine if they verify the same behavior — split the test if they don't.
Production code wants DRY. Test code wants DAMP — Descriptive And Meaningful Phrases. A test is a specification, so it has to be readable straight through without chasing helpers.
shared factory.
connection, an auth token, a temp directory). Never for the values the assertion depends on.
beforeEach ormake_fixture(), the abstraction cost more than the duplication did.
Duplication across tests is acceptable whenever it keeps each test independently understandable.
Default to real objects. Only introduce doubles when the real dependency is:
Then pick the least fake thing that works:
real > fake > stub > mock
highest confidence ..... lowest confidenceyou control).
gateway, email, third-party API.
Each step to the right buys speed and pays for it in confidence. A mock-heavy suite goes green while production breaks.
Verify state, not interactions — the classical, or Detroit, approach:
# Good — asserts the outcome
tasks = list_tasks(sort_by="created_at", order="desc")
assert tasks[0].created_at > tasks[1].created_at
# Bad — asserts the mechanism
list_tasks(sort_by="created_at", order="desc")
assert db.query.called_with(containing("ORDER BY created_at DESC"))The second test breaks when you swap the query builder, even though the behavior never changed. A suite that fails on refactors nobody broke is a suite people stop trusting.
You can't safely refactor untested code. The approach:
TDD requires knowing what "correct" looks like. If you don't:
Never let spike code become production code.
/\
/E2E\ few, slow — critical user journeys only
/------\
/ Integr \ moderate — components work together
/----------\
/ Unit (TDD)\ many, fast, cheap — TDD's home
/--------------\TDD unit tests are the foundation but not sufficient alone. Combine with integration and acceptance tests for full coverage.
Each of these shows up exactly when the discipline would have paid off.
| Rationalization | Reality |
|---|---|
| "I'll write the test after the code works" | Test-after tests the implementation you just wrote, bugs included. The design pressure is already spent. |
| "This is too simple to break" | Simple code accumulates conditions. The test is what records the intended behavior. |
| "TDD is slower" | Slower for this change. Faster for every later change to the same code. |
| "I verified it by hand" | A manual check does not persist and does not run in CI. Tomorrow's edit breaks it silently. |
| "Tests are green, that's good enough" | Green that was never red proves nothing — you never watched the test fail. |
| "It's only a prototype" | Prototypes ship. This is where test debt starts. |
| "Let me re-run the suite to be sure" | A repeat run on unchanged code adds no information. Re-run after an edit, not for reassurance. |
| Pitfall | Fix |
|---|---|
| Skipping refactor step | It's mandatory — the #1 TDD failure mode |
| Tests depending on each other | Each test must be fully independent |
| Testing implementation, not behavior | Test interfaces and outputs, not internal calls |
| Over-mocking | Default to real objects; mock only what's genuinely awkward |
| Writing code before a failing test | If there's no red, go back and write the test |
| Editing the test's expected value to match what the implementation produced | Tests specify the contract; implementation conforms to tests. If the assertion was wrong or over-specific, change it deliberately (back to red, then green again) — not silently while debugging to green. If the exact value was incidental, assert the looser contract (type, shape, key invariant) instead of pinning a string you guessed at. |
or syntax error
xfailed, or commented out to reach greenReport the command you ran and what it printed. "Tests should pass" is not a result.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.