skip to content

What must an automated case assert after it backgrounds an app mid-form and brings it back?

level: juniorimportance: must knowfreq 62%

answer

  1. Reopening is not an assertion
  2. Name what the person would lose
  3. Capture expected values before leaving
  4. Compare against what renders, not memory
  5. Assert no duplicate submission on return

basics

~20 s

Assert the specific values a person would lose - the text already typed, the step reached in the flow, the selections made, any running countdown. Checking only that the app reopened proves nothing about restoration.

solid answer

~40 s

Backgrounding matters only because of what it can destroy, so the case has to name that first. Capture the exact **product state rendered on screen** - typed text, chosen options, the step of a multi-step flow, scroll position, an active countdown - then send the app to the background, wait a named duration, and return through the resume path rather than starting the app fresh. Compare every captured value against what the returned screen now renders, not against fields held in the app's memory. Also assert the negatives: no duplicate submission fired on return, no unsaved input overwritten by a refresh, no value re-displayed that the product promised to mask. A case that only checks the app is still running has asserted nothing, because that stays true whether or not restoration works.

code

pseudocode · 14 lines
pseudocode
case "expense draft survives a background and return":
    open(screen = "new expense")
    type(field = "description", value = "team offsite dinner")
    choose(field = "category", value = "meals")

    expected = read_rendered(["description", "category", "step"])

    send_app_to_background()
    wait(seconds = BACKGROUND_SECONDS)      # named, not incidental
    resume_app_from_switcher()              # not a fresh start
    wait_until(screen_settled)

    assert read_rendered(["description", "category", "step"]) == expected
    assert submissions_for("new expense") == 0

go deeper

for a junior

Be ready to say what backgrounding can destroy: unsaved typed text, selections, and the step reached in a flow. Know that the app reopening without a crash is not evidence that anything was restored.

for a middle

Explain the difference between a return where the process stayed resident and one where the platform reclaimed it, and why only the second exercises rebuilding. Show how you capture expected values before leaving and compare them with what renders on return.

for a senior

Demonstrate that you assert the negatives too - no duplicate submission, no masked value re-shown, no refresh overwriting unsaved input. An interviewer wants a case that is real evidence about the flow, not a smoke check that the app survived.

for a principal

Own the policy rather than the case: which journeys are contractually required to survive a return, who decides that, and where it is recorded so cases are not invented screen by screen. Argue where restoration is worth its complexity and where discarding cleanly is the better product decision.

## Two behaviours share one name When someone leaves an app - pressing the home control, opening the recent-apps switcher, taking a call - the app stops drawing. What happens after that is not one behaviour but two, and a case that does not say which one it exercises has not really asserted anything. In the first, the process stays resident. Everything the app held in memory is untouched, the return is instant, and the screen the person left is the screen that comes back. In the second, the platform reclaims the process to free memory for something else, and the return is a **rebuild**: the screen is reconstructed from the small state snapshot the platform preserved plus whatever the app itself wrote to storage. A short background almost always lands in the first case. That is still worth testing, because work scheduled to run on return - a data refresh, a re-authorization check, a re-subscription to a live feed - can overwrite exactly what the person had just typed. But it proves nothing about rebuilding, and naming such a case *survives being shut down* is the most common form of coverage inflation on this subject. ## Write the expectation before you leave The case is a comparison, so the expectation has to be captured while the values still exist. Drive the flow to the point that matters, read the values off the rendered screen, hold them, and only then send the app to the background. A case that backgrounds first and improvises an assertion afterwards tends to assert whatever happened to survive, which is a tautology. | What the person built | Where it usually lives | What the case asserts on return | | --- | --- | --- | | Text typed into a field | in memory, unsaved | the same characters render in the same field | | The step reached in a multi-step flow | in memory or a preserved snapshot | the same step renders, earlier answers intact | | Selections, toggles, chosen dates | in memory | every selection still shown as chosen | | Scroll position in a long list | in memory | the same region is visible, not the top | | A running countdown | derived from a start instant | remaining time consistent with elapsed wall time | | Data fetched from a service | a cache | refreshed, or shown with the staleness treatment the product promises | | A submission already sent | held by the service | exactly one submission exists, never a second | ## Return the way a person returns Bringing the app back by starting it fresh is a different journey from resuming it, and the two can land on different screens. Use the resume path the platform offers, and make the length of the background an explicit, named value in the case rather than an accident of how long the previous step happened to take. If the intent is to reach the rebuild path, force it deliberately rather than hoping that waiting longer will get there: waiting is not a mechanism, and a case built on it passes for the wrong reason most days and fails for the wrong reason occasionally. ## Assert on what is rendered, and assert the negatives Read the values back from the rendered screen, not from the app's internal fields. The two diverge exactly where the defect lives: values can be restored into memory correctly and never re-bound to the screen, or bound to a screen that was rebuilt underneath the one now on top. Reading internals also welds the case to structures that change without any visible change at all. Then add the assertions that say what must **not** have happened: - **No duplicate submission.** Returning must not re-send a request that already succeeded. - **No silent overwrite.** A refresh on return must not replace unsaved input with values from the service. - **No leaked value.** Anything the product promises to mask must still be masked after the return. - **No advanced step.** The flow must be where the person left it, not one step further because a resume handler ran twice. - **No orphaned work.** An upload or a recording that was in progress either continues or fails visibly. ## Naming the case honestly The name should carry the experiment. *Draft survives a brief background* and *draft survives a process rebuild* are different cases with different mechanisms and different assertions, and a suite holding only the first should not read as though it holds the second. When someone later asks whether the product is safe against being stopped in the background, the answer ought to be legible from the case names alone - which is only true if each name states the condition it actually created.

  • What should the case do about data the screen had fetched from a service before it was backgrounded?
    Take the rule from the product and assert it. If the screen must refresh on return, assert that a fetch happened and that the rendered values moved to the new data. If it may keep showing what it had, assert the staleness treatment the product promises - a timestamp, a refresh control, a banner. Asserting nothing here is how a stale price or a stale balance reaches someone.
  • Why assert on the rendered screen rather than on the values the app holds internally?
    Because a person only ever sees what is rendered, and the two diverge exactly where the defect is. Values can be restored internally and never re-bound to the screen, or bound to a screen rebuilt underneath the one now on top. Reading internals also welds the case to structures that change without any visible change, so it breaks on refactors and passes on real regressions.
  • How long should the app stay backgrounded in this case?
    Long enough to be a meaningful test of the behaviour you named, and written as a named value rather than left to whatever the previous step took. A brief pause exercises the return of a still-resident app. Proving the rebuild path needs the process actually stopped, which should be forced deliberately rather than reached by waiting longer and hoping.

Leaving a room and coming back is not the same as the room being cleared and rebuilt from a photograph while you were away, and only the second one tests anything.

saying these in an interview costs you the question

  • Asserts only that the app reopened without crashing
  • Never captures the expected values before backgrounding
  • Starts the app fresh and calls it a background test
  • Reads restored values from internals instead of the screen
  • Ignores a duplicate submission fired on return
  • Assumes a two-second background exercises the rebuild path