skip to content

Execution Runtime

What happens while a run executes: who owns a driver session, which state survives between cases, the unit of parallelism, and where a retry sits. Parallelism is what exposes each choice.

on this pageshow

questions

15

Should a driver session be created per case, per class, or per parallel test worker, and what does each cost?

level: juniorimportance: must knowfreq 66%

answer

  1. Two costs pulling in opposite directions
  2. Ask what the previous case left behind
  3. Acquisition cost times cases, over workers
  4. Longer lifetime, wider blast radius

basics

~20 s

Default to a fresh driver session per case: it is the only lifetime that guarantees a clean start. Reuse across a class or a parallel worker only when acquisition is measurably slow; one case can then poison the next.

solid answer

~50 s

The choice trades **acquisition cost** against **contamination cost**. A session acquired per case is the only lifetime where every case provably starts from a known condition, and its blast radius is one case. A session shared across a class or held for the life of a parallel test worker pays the acquisition price once, but every later case inherits whatever the previous one left behind, so a single bad case turns into a run of unrelated red. Decide it with a measurement, not a preference: time one acquisition, multiply by the case count, divide by the number of workers, and compare that against the cost of investigating one false failure. Before trading isolation away, try to make acquisition cheaper — hoisting the expensive preparation and applying it at each acquisition keeps per-case isolation at most of the speed.

code

pseudocode · 12 lines
pseudocode
# per-case lifetime: acquisition and release are scope-bound
for case in cases:
    session = acquire_session(profile = prepared_profile)
    scope.on_exit(release, session)   # runs however the case ends
    run(case, session)

# per-worker lifetime: acquired once, reused, reset between cases
session = acquire_session(profile = prepared_profile)
worker.on_exit(release, session)
for case in cases_for_this_worker:
    reset_to_neutral(session)         # now an obligation you own
    run(case, session)

go deeper

for a junior

Be ready to name the three lifetimes and say which one guarantees a clean start. The short version interviewers want: fresh per case is slower and isolated, shared is faster and can carry problems forward.

for a middle

Explain the trade with numbers rather than opinion: acquisition cost times case count over worker count, weighed against the cost of a failure that lands on the wrong case. Know that reuse creates a reset obligation you then own.

for a senior

Show the diagnosis angle. Describe how a shared session produces order-dependent red that is not reproducible alone, and the mitigations you would put in place: discard a failed case's session, narrow the sharing scope, hoist the expensive preparation instead of the connection.

for a principal

Own the policy for the whole suite. Say what the default lifetime is, what evidence lets a team deviate from it, and how you keep the exceptions from spreading once someone finds that sharing makes their run look fast.

## What a session lifetime actually decides A **driver session** is the live connection a case holds to the thing it operates: a client object on the test's side, plus whatever the other side keeps on the test's behalf — a window, a process, a connection, a working directory, a cache of settings that were applied once. Its **lifetime** is the scope at whose end that connection is released. Three scopes cover nearly every suite: - **Per case** — acquired before the case runs, released when it ends, pass or fail. - **Per class or file** — acquired once for a group of cases that run together, released when the group ends. - **Per parallel test worker** — acquired once when the worker starts and released when it stops, so every case that worker runs shares one connection. This is not a style preference. It is a trade between two costs that move in opposite directions: **creation cost**, paid once per session, and **contamination cost**, paid by every case that inherits a session another case already used. ## The three lifetimes side by side | Lifetime | Sessions per run | Clean start | Blast radius of one bad case | Typical fit | |---|---|---|---|---| | Per case | one per case | guaranteed for every case | the case itself | the sane default | | Per class | one per group | first case of the group only | the remaining cases in that group | creation is slow, the group is cohesive | | Per worker | one per worker | first case on that worker only | every later case that worker runs | creation genuinely dominates the run | Read the third column as the real product of the decision. A per-case lifetime is the only one that makes the sentence *"this case starts from a known state"* true by construction rather than by hope. Every longer lifetime replaces that guarantee with an obligation: something now has to put the session back to a neutral condition between cases, and that reset is code you have to write, maintain and prove. ## Put a number on the creation cost before trading isolation away Reuse is almost always argued for on speed, and the argument is usually made without a measurement. Do it in four steps: 1. **Time one acquisition** in isolation — not the whole first case, just the session coming up. 2. **Multiply by the case count and divide by the number of parallel workers.** Suppose acquisition takes 4 seconds, the suite has 900 cases and 6 workers run it: `900 * 4 / 6` is 600 seconds, so per-case sessions add about ten minutes of wall clock. 3. **Compare that against the cost of one contaminated failure.** A single false failure that is investigated by the wrong person, reproduced three times and eventually traced to a neighbouring case costs a working morning. Ten minutes of machine time is cheap next to it. 4. **Attack the creation cost first.** Much of what makes acquisition slow is not the connection itself but the preparation piled on top of it. Hoisting the expensive *preparation* — building a reusable starting profile once and applying it at each acquisition — keeps a fresh session per case while paying the expensive part once. ## What reuse actually hides The failure mode of a long-lived session is not that it fails. It is that it **moves the failure**. A case that puts the application into a bad condition and then passes leaves the next case to fail, and the next case's owner debugs a symptom that has nothing to do with the change they made. Three consequences follow, and all three are why a reused session is expensive even when it is fast: - **Failures stop being reproducible alone.** Running the failing case by itself passes, which is the most demoralising signal a suite can send. - **Blame lands on the wrong commit.** The case that appears red is chronologically after, not causally related to, the one that broke. - **Order becomes load-bearing.** Once a suite only passes in one order you have lost the freedom to shard it, re-run a subset, or run a single case locally. ## A middle path worth knowing When acquisition is genuinely expensive and per-case is genuinely unaffordable, the useful compromise is rarely "share for the whole run". Prefer the narrowest reuse that pays: share within a small, cohesive group whose cases were written together and are read together, and make the group's first act an explicit assertion that it starts from the condition it expects. Add one more rule: **any case that fails discards its session instead of handing it on.** A failed case is precisely the one most likely to have left something behind, so retiring its session removes the single largest source of downstream contamination while keeping most of the speed. That rule costs one extra acquisition per failure and buys back most of the isolation you traded away.

  • A suite has 900 cases, acquisition takes four seconds, and six parallel test workers run it. How do you decide whether per-case acquisition is affordable?
    Compute it: 900 acquisitions at four seconds is 3600 seconds of work spread over six workers, about ten minutes of wall clock. Weigh that against one contaminated failure, which typically costs an engineer a morning. Ten minutes of machine time is usually the cheaper side, and if it is not, attack the four seconds before you give up isolation.
  • If a case leaves the application on an error screen and the session is shared across its class, what happens next?
    The following cases in that class start from that error screen and fail at their first interaction, before they touch their own subject. The suite reports several failures, none of which is the real one, and each is investigated by whoever owns that case. The first red case in the group is the only one worth reading.
  • When is sharing a session across a group of cases actually defensible?
    When acquisition is genuinely expensive, the group is small and cohesive, its cases were written and are read together, and the group's first act asserts the condition it expects. Add one rule: a case that fails discards its session rather than handing it on, since a failed case is the most likely one to have left residue behind.

A session per case is a fresh workbench for every job; a session per worker is one bench everybody shares, where the last person's mess becomes your first problem.

saying these in an interview costs you the question

  • Claims reusing one session across the whole suite is always faster overall
  • Cannot name anything a reused session carries from one case to the next
  • Treats acquisition cost as fixed and never measures it
  • Picks a lifetime without asking how a contaminated failure gets diagnosed
  • Thinks a per-case session is unaffordable without ever timing one
open as a page

A parallel run can dispatch by case, by class or by process — what does each unit make contended?

level: middleimportance: must knowfreq 58%

basics

~20 s

The unit of parallelism decides what two test workers can touch at the same moment. Dispatching whole classes protects per-class setup; dispatching individual cases contends it; separate processes end in-memory sharing but leave every external resource shared.

open as a page

Where can a retry sit in an automated suite — around an action, around a case, or around a whole run — and what does each placement conceal?

level: middleimportance: must knowfreq 62%

basics

~20 s

A retry can wrap a single action, a whole case, or an entire run. The wider the wrapper, the more it conceals: an action-level retry hides that a step was unstable, a run-level retry hides which case failed at all.

open as a page

A harness keeps the current case's test user in one process-wide variable; why does that break once two test workers run at once?

level: middleimportance: must knowfreq 60%

basics

~20 s

Both workers write the same variable, so a case reads whatever the other stored last. Serial execution was the only thing making that safe. The result is wrong-data assertions and cross-talk between cases, not a crash.

open as a page

What do you gain by passing a case's test data into a helper as parameters instead of letting the helper read a shared holder?

level: juniorimportance: should knowfreq 40%

basics

~20 s

The helper's inputs become visible in its signature: a reader knows what it needs, a caller can supply different data, and nothing outside the call can change it midway. The cost is longer signatures on every step of the chain.

open as a page

A case opens a driver session, then a preparation step throws before the first assertion. What guarantees the session is closed?

level: middleimportance: should knowfreq 47%

basics

~20 s

Only a release registered at the moment of acquisition, bound to a scope the runner unwinds however the case ends. A close on the case's last line runs on the success path only, so an earlier throw leaks it.

open as a page

When a driver session is reused across cases, what must be reset between them, and what breaks when it is not?

level: middleimportance: should knowfreq 54%

basics

~20 s

Reset everything the driver session carries: stored client data, the current location, open overlays, granted permissions, display size, and any per-case connection settings such as a shortened wait budget. Miss one and you get order-dependent failures in the wrong case.

open as a page

What must each parallel test worker hold uniquely for a case to run safely beside a copy of itself?

level: middleimportance: should knowfreq 48%

basics

~20 s

Anything the case names by a constant is a collision candidate: the identity it signs in as, any fixed local port it binds, the paths it writes evidence to, and any single record or global setting it changes in place.

open as a page

Why should an automated suite record a pass on a second attempt as a distinct outcome rather than a plain pass?

level: seniorimportance: should knowfreq 46%

basics

~20 s

A pass that needed a second attempt is evidence that a case is unstable. Merging it into a plain pass destroys the only proof that instability exists, so the suite keeps reporting green while its reliability quietly decays.

open as a page

A case fails only when the suite runs with several test workers, yet passes alone — how do you find the shared harness state?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Treat worker count as a variable you control: reproduce at two workers, shrink to the smallest failing pair, then run the case beside a copy of itself. Then hunt the writer, not the reader that reported red.

open as a page

One long-lived deployed target serves every case and cannot be duplicated per test worker — how do you set the suite's isolation model?

level: principalimportance: should knowfreq 40%

basics

~20 s

Choose between two models: hand each worker its own slice of a scope the product already models, or run a wide pool of self-contained cases beside a small exclusive lane holding named locks. What the product scopes decides which.

open as a page

How does a suite that leaks driver sessions show itself before the run host runs out of resources?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

Run duration climbs within a single run, host resource use rises and never falls back between cases, orphan processes survive the run, and late cases fail with resource errors that name the machine rather than the feature.

open as a page

Why is a suite that passes only when run serially a defect rather than a configuration choice?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

Running with one worker changes no code: it removes the only thing exercising the coupling, not the coupling itself. The constraint stays unnamed, its cost grows with every case, and it may hide interference real users will meet.

open as a page

In an automated suite, what does a retry budget for the whole run protect that a per-case retry cap does not?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

A per-case cap bounds one case's attempts and nothing bounds how many cases retry. A run-level budget caps total re-execution across the suite, so a suite-wide breakage reports red quickly instead of paying the full cap on every case.

open as a page

Giving each parallel test worker its own current-case context fixes cross-talk; what does a helper that reads that context implicitly still cost you?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

Scoping removes the mix-up, not the hidden dependency. A helper that reaches for an ambient current-case value has an input its signature does not declare, cannot be reasoned about locally, and misbehaves wherever no case is in scope.

open as a page