skip to content

Mobile Test Design

How a case changes on a handheld: choosing the device and version matrix, covering gestures, permission prompts and interruptions, testing on field networks. Interviewers probe what you would cut.

on this pageshow

questions

30

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
open as a page

Why do a tap and a long press on the same list row need separate automated cases?

level: juniorimportance: must knowfreq 58%

basics

~20 s

A long press is a distinct input event that opens behaviour a tap never reaches: a context menu, selection mode, a drag handle. A tap case proves only the primary action, so the held gesture needs its own case.

open as a page

Why can't a handheld app's automated suite just target the newest platform version?

level: middleimportance: must knowfreq 58%

basics

~20 s

Handheld users choose when to update, and many devices stop receiving platform updates entirely, so the installed base stays spread across several version lines for years. A suite that runs only on the newest line leaves most real users unverified.

open as a page

A disconnected mobile client queues user actions and delivers them on reconnect - what does an automated case assert at each of the three moments?

level: middleimportance: must knowfreq 62%

basics

~20 s

Assert three times: while disconnected, that the action is locally accepted and marked pending; at reconnect, that delivery starts unprompted; once drained, that the far side holds the work and the device now matches it.

open as a page

A sign-up form is half filled in when the device is rotated mid-flow. What must the case assert after rotation?

level: middleimportance: must knowfreq 58%

basics

~20 s

Assert the in-progress input survives: values already typed, the selection, the scroll region, any open dialog, and the step of the flow. Rotation rebuilds the screen, so a case that only checks it still renders proves nothing.

open as a page

Why does a system permission prompt need grant, deny and permanent-deny as three separate cases?

level: middleimportance: must knowfreq 60%

basics

~20 s

Three answers produce three different application behaviours: the feature works, the feature degrades and may ask again, and the feature must degrade with no prompt ever appearing. Only the third exposes the silent dead ends.

open as a page

A staged rollout leaves two app versions live against one backend. What must your test coverage prove about that overlap?

level: seniorimportance: must knowfreq 58%

basics

~20 s

Coverage must prove the previous app version still works against the backend the new one requires. Run both versions' suites against the same deployed service in one pass, and cover a user who updates partway through a flow.

open as a page

On a degraded link, why is asserting only that the screen eventually loads a weak case?

level: juniorimportance: should knowfreq 48%

basics

~20 s

Eventual success only proves the happy path survives slowly. Assert what the user sees meanwhile and on failure: a progress surface, a control that cannot fire twice, a notice naming the next step, and typed data preserved.

open as a page

A disconnected screen still renders records loaded earlier - how does a case tell stale-but-allowed from simply wrong?

level: juniorimportance: should knowfreq 46%

basics

~20 s

Capture the values the device holds before disconnecting; that snapshot is the oracle. Stale-but-allowed means the screen matches it and says it is not current. Wrong means values never held, missing promised content, or stale data shown as live.

open as a page

A case asserts an element sits at fixed screen coordinates. Why does a raised system text-size setting break it, and what should it assert instead?

level: juniorimportance: should knowfreq 42%

basics

~20 s

Larger text reflows the layout: everything moves, wraps and grows, so a coordinate no longer identifies the element. Assert on identity and content instead — the element is present, reachable, holds the expected text, and is not drawn cut off.

open as a page

Why is a permission prompt drawn by the operating system not just another dialog in your application?

level: juniorimportance: should knowfreq 48%

basics

~20 s

The prompt belongs to the platform, not the product. Its wording and layout are decided outside the application, it may not appear at all when a decision is already stored, and it takes focus away from your own screens.

open as a page

Why can a suite pass on the build your pipeline makes for testing and fail on the build users install?

level: juniorimportance: should knowfreq 46%

basics

~20 s

They are different artefacts. The shipped build is optimized, has unused code stripped, is signed with the release identity, drops test hooks and verbose logging, and points at production configuration — so code the test build kept alive can vanish from it.

open as a page

When a case enters a journey through a deep link that skips earlier screens, what must it arrange first?

level: middleimportance: should knowfreq 38%

basics

~20 s

Everything the skipped screens would have produced: a signed-in user established programmatically, a target record seeded for this run, any first-run or consent flags those screens write, and a link built from that seeded record.

open as a page

What makes a swipe or drag case reproducible when direction and speed change the outcome?

level: middleimportance: should knowfreq 46%

basics

~20 s

Declare the gesture in terms the case owns: endpoints as fractions of the target element, a stated direction, and an explicit contact duration. Recorded pixel coordinates and default speeds differ per device, so one script runs a different gesture.

open as a page

After the system reclaims a backgrounded app's process, how should a case prove the screen rebuilds?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Force the termination deliberately rather than waiting for it, re-enter through the path a returning person takes, and assert the rebuilt screen carries the same record and the same answers - while checking that nothing half-finished silently resumes.

open as a page

What does a handheld suite that only runs on current top-tier devices never see?

level: seniorimportance: should knowfreq 46%

basics

~20 s

A top-tier-only suite never sees failures caused by scarcity: the app's process reclaimed while in the background, cold starts slow enough to expose races, memory limits hit on large content, and a device slowed to shed heat.

open as a page

Why can acting on an off-screen element that the element tree reports as present still fail?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Presence in the element tree is not the same as being touchable. A contact is delivered to a screen coordinate, so an element scrolled out of view or covered by a fixed bar receives nothing, or the wrong element does.

open as a page

A request is in flight when the handset switches connection type - what must the case control and assert?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Control the moment: hold the response at a stand-in the run owns, switch the connection, then release. Assert the ambiguous outcome resolves - one definite result on screen and exactly one matching effect recorded on the server side.

open as a page

A device edits a record while disconnected and the far side changes the same record - what must the case pin down about the resolution?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Four things: how the divergence was constructed, which value the written rule says survives, what became of the losing edit, and that device and far side agree afterwards. Assert the recorded rule, never the behaviour you happened to observe.

open as a page

When a case denies a capability permission, what should it assert beyond the application not crashing?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Assert the whole degraded journey: the fallback appears, explains what is unavailable, and can be completed to a real end state. Then assert what must not happen — no repeated prompting, no empty screen passed off as success, no dead controls.

open as a page

Your handheld target list has not changed in two years. How do you decide what it should run on now?

level: principalimportance: should knowfreq 40%

basics

~20 s

Re-cut it from field evidence rather than habit: usage-weighted share across version lines, capability tiers and screen classes, and which rows have ever caught a failure nothing else caught. Every re-cut costs comparability, so change it on a decided cadence.

open as a page

After an incoming call covers a flow and is dismissed, what should an automated case check?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

Check that the flow resumed at the same step with the same entered values, that nothing advanced or re-sent itself while the app was inactive, that countdowns and media behaved as specified, and that nothing sensitive was left exposed.

open as a page

Why is screen-size class a separate coverage axis from a handheld's capability tier?

level: middleimportance: nice to knowfreq 28%

basics

~20 s

Screen size and device capability vary independently: large low-cost displays and small powerful devices both exist. Layout failures — clipped labels, controls out of reach, a keyboard covering the focused field — track the screen class, not the hardware's speed.

open as a page

What does a touch-driven case fail to prove about a flow that also accepts keyboard or pointer input?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

Only that the touch path works. Keyboard and pointer input arrive as different events - hover, secondary click, key repeat, an arrow-key caret - and reach code the touch path never runs, so gesture-only actions stay unexercised.

open as a page

An unsynced-work indicator on a disconnected client - why is it product behaviour a case must assert rather than a diagnostic?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

The unsynced-work indicator is the only report a user gets about work that has not left the device, so it is a promise, not instrumentation. A case asserts it rises, holds while work is outstanding, and clears only on success.

open as a page

A mobile app blocks its own use below a minimum supported version. How do you design cases proving the gate cannot be bypassed?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Test the boundary and the ways around it: at the minimum version, one below it, and with the version check unreachable. The decisive case asserts the service refuses a blocked client, since a screen the app draws can always be skipped.

open as a page

How do you test an app on a shared network that answers requests with its own sign-in page?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Stand a programmable element in the request path that answers every call with a sign-in page instead of the expected payload. Assert the app rejects the impostor response, points the user at the network, and caches nothing.

open as a page

When a product runs in split-screen beside another app, which single-window assumptions break, and how would you test them?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Split-screen halves the available width and can resize the app while it runs, so layouts assuming a fixed full-screen size, permanent focus, or exclusive use of hardware break. Test at the narrow size and while the divider is dragged.

open as a page

How do you test the path where a permission the application already holds is revoked mid-use?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

Take the permission away between two uses of the feature inside one case, then assert the second use lands on the degraded path rather than erroring or hanging. Products fail here by checking once at launch and caching the answer.

open as a page