In a feature where a generative step picks which internal action to run, how do you assert that dispatch exactly?
answer
- Watch the hand-off, not the answer
- Record the action name and arguments
- Assert count of dispatches too
- Pin the request so it repeats
basics
~20 sObserve the hand-off between the decision and the action: record the selected action's name and its argument values, then assert those against exact expected values. Pin the request so the case repeats, and assert nothing about the text that came back.
solid answer
~50 sPut an observation seam at the dispatcher — the point where the chosen action's name and arguments are handed to the code that executes it — and have the test read what was recorded there. Assert **equality on the action name**, equality on each argument after normalising anything clock- or identity-derived, and that no second action was dispatched for the request. Pin the request text and every input the decision depends on, so the case gives the same answer today and next quarter. Assert nothing about the returned prose in the same case; the moment it reads the answer text it inherits that text's variance and stops being a reliable signal. If the action has side effects, assert the effect too — a booking row created once, not twice. This is one case with one expected dispatch, not a measurement taken across repeated calls.
code
pseudocode · 12 linesgiven request = "book the nine o'clock slot for tomorrow"
given today = fixed_date("2030-04-01")
record = dispatcher.observe():
run_feature(request)
assert record.count == 1
assert record[0].action == "create_booking"
assert record[0].args.startsAt == "2030-04-02T09:00"
assert bookings.rows_for(user) == 1
# deliberately absent: any assertion over record.answerTextgo deeper
Know that a feature can be checked for where it sent the work, not only for what it said. Be able to say why reading the answer text to decide whether the right action ran is unreliable.
Explain the seam: a dispatcher records the selected action and its arguments before execution, and the test asserts equality on both plus the number of dispatches. Expect to describe how the request is pinned.
Show the production judgement: which arguments must be normalised rather than dropped, why an extra dispatch has to fail the case, and how a case stays stable when only the wording around it changes.
Own the policy on seams. Decide where observation points are permitted, whether expectations may ever be recorded from live behaviour, and how the team keeps tested paths identical to shipped paths.
## The seam is the whole trick When a generative step inside a product decides which internal action to run, the decision and the execution are two different things separated by a hand-off. That hand-off is the seam: a dispatcher receives an action name and a set of argument values and calls the corresponding code. A test that can observe the seam can assert the decision without ever looking at the prose the feature eventually shows. Three things make a usable seam. It must be **recordable** — the dispatcher writes what it received somewhere the test can read. It must be **before execution**, so an action that later fails still leaves a record of having been selected. And it must be **complete**, capturing every dispatch in the request, not only the first, otherwise a duplicate or a stray extra action passes unnoticed. ## What one case fixes | Element | Handled how | | --- | --- | | Action name | asserted by equality against one expected name | | Argument values | asserted by equality after normalising clock- and identity-derived fields | | Number of dispatches | asserted exactly, usually one | | Side effect of the action | asserted by its own exact evidence, such as one stored row | | Text returned to the user | not read by this case at all | The last row is the discipline that keeps the case alive. A case that asserts the dispatch **and** a phrase from the answer fails on any rewording, and once it has cried wolf a few times its dispatch assertion is discarded along with it. ## Making it repeatable - **Pin the request.** Fix the exact wording of the user input the case sends. If the input is assembled from the current date or a random selection, the decision can change underneath you and the case becomes intermittent for reasons that have nothing to do with the code. - **Pin the surrounding inputs.** The available actions, the caller's permissions, the account state and anything else the decision reads should be fixed by the case's setup. - **Normalise, do not ignore.** Where an argument carries a timestamp or a generated identifier, assert its shape and its relationship to the fixed setup rather than dropping it from the assertion, or a dropped argument becomes an untested argument. - **Keep the expectation literal.** The expected action name and arguments are written in the case, not derived by running the feature and recording whatever came out. A recorded expectation blesses today's behaviour, including today's bug. ## Ways this case goes wrong 1. **Asserting through the answer text.** Concluding that the right action ran because the answer mentions a booking is inference, not assertion; the prose can say "booked" while nothing was dispatched. 2. **Asserting only the name.** The right action with the wrong arguments is one of the most damaging failures in this kind of feature and is invisible unless arguments are asserted. 3. **Allowing extra dispatches.** If the assertion only checks that the expected action appears somewhere in the record, a feature that also fired a second, costly action still passes. 4. **Leaving the seam in production code as a switch.** The seam should be an observation point, not a behaviour flag that makes the tested path different from the shipped one. 5. **Silently absorbing a changed decision.** If the case is regenerated from actual behaviour whenever it fails, it stops being a test. ## What this case is not It is not a measurement of how often the decision is right. Running many varied requests and scoring how frequently the intended action was chosen is a different exercise with different machinery, and it answers a question about the generative step rather than about the product. The case described here answers a narrower and more useful question for an ordinary suite: *for this one fixed request, did the software send the work to the right place with the right values, exactly once?* That question has a yes or no answer, it stays stable across rewording, and when it turns red something concrete has broken. It is also not a check on how well-formed the decision's output was. Whether the generative step reliably produces arguments of the right shape belongs elsewhere; here, malformed arguments simply make the equality assertion fail, which is the correct outcome for a product suite.
- The dispatched action's arguments include a generated identifier that differs on every request. How do you assert it?Assert its relationship to the fixed setup rather than a literal value: that it matches the identifier the case created, that it has the expected shape, or that the same value reaches the downstream store. Dropping it from the assertion turns a real argument into an untested one, which is exactly where a wrong-record defect hides.
- How do you keep this case honest when a product change legitimately alters which action should be selected?Edit the expected action and arguments in the case as part of the same change, the way any contract update is made deliberately. What must never happen is regenerating the expectation from whatever the feature now does, because that blesses the new behaviour without anyone deciding it was intended.
saying these in an interview costs you the question
- Infers the action ran from wording in the answer
- Asserts the action name but never its argument values
- Lets extra dispatches pass because the expected one appears
- Rebuilds the expected dispatch from whatever the feature just did
- Leaves the request text unpinned and calls the case intermittent