A mock assertion is useful when it observes a requirement that matters outside the implementation. Checking the recipient, payload, and number of outbound messages can establish part of a notification contract. Checking that two private helpers were called can leave the message itself almost entirely unexamined.
Begin a test review by asking what plausible wrong behavior would make each assertion fail. If the answer is only “the helper was renamed,” the test may be protecting an implementation detail while allowing a real regression through.
Write the contract before judging the mock
Consider an illustrative invoice-notification service. Suppose its agreed contract requires an authorized request, a message addressed to the invoice's intended recipient, the correct invoice reference and amount, and one outbound submission for a successful request.
Suppose it also requires rejection without a send when authorization fails. For provider failure, assume the service must report failure to its caller and leave the notification eligible for the documented recovery path.
These assumptions need confirmation in the actual system. Define what “successful” means at this boundary: provider acceptance, durable scheduling, and final delivery are different events. A test should assert the promise the service actually makes.
Identify what the weak test permits
A test that invokes the service and checks only that rendering and sending occurred leaves several mistakes possible. The service could render the wrong invoice, send to the wrong address, submit twice, or swallow a provider failure.
Name those escaping regressions in the review. That gives the author a concrete reason to strengthen the assertions and keeps the discussion focused on behavior.
Coverage can help locate unexercised code. It cannot establish that a visited path produced the correct result. A passing assertion that merely approves any call remains weak even when the surrounding code has high coverage.
Inspect the interaction's meaningful arguments
When sending is part of the contract, check the recipient and relevant payload fields. Use fixtures that distinguish the requesting user, invoice owner, and an unrelated recipient so a mistaken lookup cannot pass through equal addresses.
Give the invoice a distinctive reference and amount. Assert the contract-relevant content at the boundary where it becomes observable. If an amount has a formatting contract, compare the expected representation there.
Avoid freezing incidental details such as private helper order or unrelated formatting. Include call order when the external protocol requires it. State that requirement so future maintainers can distinguish an intentional constraint from an accidental reflection of today's implementation.
Include call counts and required non-calls
For the stated successful-request contract, verify one outbound submission. A broad “was called” assertion would also pass if the code sent twice.
For denied authorization, verify the expected failure outcome and no outbound send. Check that authorization denial cannot be mistaken for an empty successful result. If the contract also forbids creating a queued notification, inspect that state separately.
Use realistic denied and allowed fixtures. A denial test that crashes on a missing fixture field before reaching authorization can appear protective while never exercising the intended boundary. Confirm why the test takes its failure path.
Pair interactions with outcomes where practical
Inspect what the caller receives and any state the service owns. Under the illustrative failure contract, a rejected provider call should produce the agreed failure result and preserve the documented recovery state.
A mock accepting a request proves only what the tested service submitted to that mock. It does not establish actual delivery, provider compatibility, or durable storage. Choose a separate integration check when those boundaries matter to the patch.
Keep the tests complementary. A focused test can distinguish the wrong recipient quickly; an integration check can exercise the real adapter's request format. Repeating the same weak “called” assertion at several layers adds little confidence.
Control failure timing deliberately
For a timeout case, control the dependency's completion and the application's timeout mechanism. Arrange a pending operation, advance the relevant test clock or scheduler to the defined boundary, and inspect the promised outcome.
Avoid relying on a short real-world delay to make the test pass. Explain which timer controls the timeout and ensure the test actually reaches that path. Restore the clock and settle pending work so later cases inherit no unfinished operation.
If late completion matters, resolve the controlled operation after the timeout and check the agreed behavior. Whether that completion is ignored, reconciled, or recorded depends on the service contract. The fixture should make the ordering explicit.
Build cases that separate plausible mistakes
A compact review table can reveal the assertion gaps:
| Case | Observable expectation | Mistake it should expose |
|---|---|---|
| Authorized successful request | Correct recipient and invoice content, one submission, agreed success result | Wrong invoice, wrong address, duplicate submission |
| Denied request | Agreed rejection and no send | Sending before authorization |
| Provider rejection | Agreed failure outcome and recovery state | Swallowing the error or recording success |
| Timeout | Defined timeout outcome under controlled timing | Returning success while the operation remains unresolved |
Adapt these cases to the actual contract. They are proposed checks, with no execution result implied. Identify what each excludes and what still requires another test or environment.
Distinguish a test rewrite from a bug fix
When reviewing weak tests, request a discriminating check that would catch a plausible regression. You can demonstrate its sensitivity by considering or introducing a controlled wrong implementation in an isolated test exercise.
An actual reproducible product defect adds a stronger requirement: retain the behavioral check that fails before the fix and passes afterward. Rewriting a weak test alone does not establish that the original product contained that defect.
Finish with specific feedback: identify the missing observation, the escaping mistake, and the proposed assertion. Record any observed results separately from the plan. Approve the tests when their assertions protect the agreed behavior and the remaining integration limits are explicit.
The Agentic Developer Interview includes the Test Quality Review card covering contract-visible outcomes, meaningful interaction assertions, failure and boundary cases, and discriminating regression checks.