agentleFS
waqarqasimwaqarqasim/portable-agent-skills/skills/audit-test-doubles/SKILL.md

audit-test-doubles

Skill0 starsChanged 2 months agoMITView on GitHub

Finds tests that pass whether or not the production code is correct, by auditing how mocks, stubs, fakes and spies are set up and asserted. Use when reviewing test quality, when a bug shipped despite green tests, when the user asks whether tests are meaningful or asks to audit, improve or review tests, or when a test suite passes and you want to know if it proves anything.

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    The question to ask of every test: if I broke the behaviour this test is named after, which line goes red?

    line 127

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Audit test doubles

A test built on a loosely configured double can pass whether or not the code under test is correct. It is not a weak test; it is a test measuring the value the test itself supplied.

Works with NSubstitute, Moq, FakeItEasy, Jest, Sinon, unittest.mock, pytest-mock, gomock and Mockito — the failure modes are the same shape in all of them.

Procedure

Given a test file, a directory, or a diff, check each of the following.

1. Stubbed loosely, never asserted

The most common and most expensive case.

// The double answers ANY call matching the shape.
_pricing.Lookup(Arg.Any<string>(), Arg.Any<Guid>()).Returns(19.99m);

var result = _sut.Quote(sku);

result.ShouldBe(19.99m);          // green even if Quote passed the WRONG id

The production code can pass the branch id where the warehouse id belongs, get the stubbed answer anyway, and the assertion on the return value still holds.

Fix — key the stub, and assert the call:

_pricing.Lookup(sku, warehouseId).Returns(19.99m);

result.ShouldBe(19.99m);
_pricing.Received(1).Lookup(sku, warehouseId);

How to find it: files that configure a double with a wildcard matcher and never assert an interaction.

# adapt the matcher/assertion names to the framework in use
grep -rl 'Arg\.Any<'  --include=*.cs tests | sort > /tmp/loose.txt
grep -rl 'Received('  --include=*.cs tests | sort > /tmp/asserted.txt
comm -23 /tmp/loose.txt /tmp/asserted.txt

Report this as a signal, not a verdict. A query test whose assertion is the returned value legitimately needs no interaction assertion. Judge each file; do not report the count as a defect count.

2. A wildcard matcher where the argument is the behaviour

Arg.Any<string>() cannot catch a typo. If the production code calls IsEnabled("prochurement.approvals"), a wildcard stub returns the happy value and the test stays green forever.

Key the stub to the exact expected value and let anything else fall to the default.

3. New interface members defaulting silently

Adding a member to an interface breaks every hand-written implementation — the compiler sees to that. It breaks no auto-generated double: those return default for the new member, silently.

The tests written for the new behaviour pass, because they stub it explicitly. Every other test now runs a path it was never meant to and asserts nothing about it.

Check: for any interface changed in the diff, find every place it is doubled and confirm the new member is stubbed explicitly — including where the correct value is the default. An explicit default is a decision; an implicit one is an accident that reads the same.

4. Over-mocking

A test that doubles everything the subject touches asserts only that the subject calls the methods the author expected, in the order they expected. It breaks on every refactor and catches no defect. Where the collaborator is pure, cheap and deterministic, use the real thing.

5. Assertions on things that are not the behaviour

  • Prose. ShouldContain("Order — pending") breaks when the string is translated, encoded, or reworded, and none of those are the behaviour. Anchor on a stable hook and the data separately.
  • Counts over growing sets. Permissions.Count.ShouldBe(47) teaches people to update the number, including on the day something is deleted by accident. Assert the rule, plus a checked-in list compared in both directions.
  • Snapshots nobody reads. An approved snapshot accepted without review records the behaviour at the moment someone stopped paying attention.

6. Setup that cannot fail

Doubles configured to throw or return a failure in setup, with no test exercising that path. Dead configuration reads as coverage.

Report

For each finding:

fieldcontent
locationfile:line
patternwhich of the above
why it cannot failthe concrete production change that would go undetected
fixthe smallest edit that makes the test watch the behaviour

Rank by what a passing version of this test currently permits, not by how many there are.

Then verify one: apply the fix, break the production behaviour it now claims to watch, and confirm it turns red. An audit that does not demonstrate at least one catch is a list of opinions.

Rules

  • A grep is a signal, not a defect count. Say so when reporting numbers.
  • Never rewrite a test to match current behaviour. That converts an assertion into a transcript, and the bug becomes the specification.
  • The question to ask of every test: if I broke the behaviour this test is named after, which line goes red? If you cannot name the line, the test is measuring something else.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.