skip to content

Why seed a store or prewarm a data cache before mounting, instead of driving the tree through the steps that would produce that state?

level: seniorimportance: should knowfreq 50%

answer

  1. precondition versus subject
  2. arrange the state, do not perform it
  3. states the interface cannot reach
  4. a seeded fiction still passes
  5. keep a few that drive the real path

basics

~20 s

Because the state is the case's precondition, not its subject. Seeding puts the tree straight into the situation under test, keeps the setup readable and deterministic, and stops every test from depending on the correctness of an unrelated flow.

solid answer

~50 s

If a test is about how a table renders forty rows, the interesting part is the rendering, not the four screens a user would walk through to load them. Seeding the store with those rows — or writing the entries straight into the data client's cache so the tree renders its loaded view immediately — makes the precondition explicit, fast and independent of unrelated code. It also lets you arrange states the UI cannot easily reach: a partially filled draft, a stale cached entry, a permission combination. The cost is that a seeded shape can be a shape the application would never produce, so the test passes against a fiction; and nothing exercises the path that writes the state. So seed for the many tests whose subject is downstream, and keep a small number that drive the real path end to end — including the one test that proves the write itself.

go deeper

for a junior

Learn the habit: put the state the test needs into the store or cache before mounting, rather than clicking through the application to produce it.

for a middle

Explain what seeding buys — determinism, readability, unreachable states — and the two failure modes: an impossible shape, and an untested write path.

for a senior

Show judgment per test: seed everything upstream of the subject, drive the subject, and build seeds from the same factory the production path uses.

for a principal

Own the balance across the suite: mostly seeded unit-level tests plus a deliberate few that walk real paths, with a stated rule for which flows earn one.

## Arranging, not performing Every test has a precondition and a subject. Component tests go wrong when the precondition is produced by *performing* it: signing in, navigating, submitting a form, waiting for a list to load, and only then asserting on the thing the test is named after. That test is slow, it is coupled to four features it does not claim to cover, and when it fails you cannot tell from the name which of five steps broke. Wiring gives you the alternative. The store and the data cache are collaborators you control, so you can put them into the state the case needs before the tree ever mounts: - **Seed the store**: hand the mount helper the state the store should hold, so subscribing components read it on their first render. - **Prewarm the cache**: write entries into the data client under the keys the tree will ask for, so its read resolves immediately and the loaded branch renders without a request. - **Position the location and provide the session** in the same breath, since "who is looking, and at what URL" is usually part of the same precondition. ## What seeding buys 1. **Determinism.** No request, no timing, no dependency on another feature's correctness. 2. **Readability.** The precondition is a literal value in the test, so a reader sees the situation being tested without reconstructing it from steps. 3. **Reachability.** States the UI cannot easily produce become one line: a stale entry, a half-completed draft, a role with one permission missing, a list long enough to paginate. 4. **Focus on failure.** When the test fails, the only untrusted code in it is the code the test is about. ## What seeding costs | Risk | How it shows up | Mitigation | |---|---|---| | An impossible state | Test asserts on a shape the application would never build, and passes forever | Derive seeds from the same factory or schema the real write path produces | | The write path untested | Every test starts after the write, so a broken write reaches production green | Keep a small number of tests that drive the path end to end | | Coupling to internal shape | A store refactor breaks hundreds of tests that hand-write its internals | Seed through the store's own public write surface or a factory, not by assembling internal fields | | A cache key that never matches | Entry written under a key the tree does not request, so the loading branch renders | Build the key with the same helper the application uses | The last one is the most common practical failure: a prewarmed cache is only warm if the key matches exactly, and a mismatch looks like a component that ignores its cache. ## Choosing per test A useful rule: **seed everything upstream of the subject, drive the subject itself.** For a test about a list's empty state, seed an empty collection. For a test about what happens when the user submits the form that creates the row, drive the form — that is the subject. For a test about an error surface, seed the error into the cache rather than orchestrating a failing request, unless the subject *is* how the request's failure becomes that state. Seeding also interacts with what the state is for. Preloaded store state is a plain precondition. A prewarmed cache is slightly different: it also encodes a claim that the data is fresh enough to use, so a test that seeds it is asserting on the loaded branch and not on how the tree behaves while a request is outstanding. If both branches matter, they are different tests with different setups. ## The reviewer's view A setup block that seeds a realistic precondition in a few lines reads as a well-factored application: the state has a shape you can state. A setup block that must click through three surfaces to reach the precondition says either that the state is unreachable except through the UI, or that the author had no seam to write it through. Both are worth fixing in the application, not worked around in the test. And the opposite extreme is its own smell: a suite where *every* test is seeded and nothing ever drives the real path has no test that proves the state can be produced at all. The balance is deliberate, not accidental — many cheap seeded tests over one subject each, plus a few that walk the path, and honesty about which kind you are writing.

  • What is the danger of hand-assembling the seeded state rather than building it from a factory?
    A hand-written literal can be a shape the application never produces — a missing field, a stale flag combination — so the test passes against a fiction. It also binds hundreds of tests to internal structure, so a refactor breaks them all. Build seeds from the same factory or write surface the real code uses, then override just the fields the case cares about.
  • A test prewarms the cache but the tree still renders its loading state. What is the usual cause?
    The entry was written under a key the tree does not request. Cache lookups are exact, and keys usually encode identifiers, parameters and sometimes the signed-in principal. Build the key with the application's own key helper rather than typing it, and assert the loaded branch rendered so the mismatch fails loudly instead of silently testing the loading path.
  • If seeding is so much cheaper, why keep any test that drives the state through the interface?
    Because nothing else proves the state is producible. A suite that always starts after the write can stay green while the write is broken, and a seeded shape can drift from the real one unnoticed. A small number of path-driving tests — one per important flow — anchor the seeded majority to reality.

saying these in an interview costs you the question

  • Walks the interface through unrelated flows to reach every precondition
  • Hand-writes seeded state as internal fields rather than through a factory
  • Seeds a combination the application could never produce and calls it covered
  • Assumes a prewarmed cache is used even when the requested key differs
  • Seeds every test and keeps none that exercises the write path
  • Treats a seeded loaded state as also covering the pending state