skip to content

Two result models place setup and teardown differently: one holds fixtures in a separate container that names its children, the other makes them typed items inside the same tree. What does each shape cost a tool that consolidates results?

level: principalimportance: nice to knowfreq 34%

answer

  1. fixtures are work, but not tests
  2. outside the result, or inside the tree
  3. one has no identity, one needs a flag
  4. a silent join versus inflated counts
  5. normalise before you consolidate

basics

~20 s

A sidecar container stores a shared fixture once but gives it no identity and a join that can silently resolve to nothing. Typed in-tree items give a fixture an id and logs, but every consumer must exclude them from counts.

solid answer

~40 s

The sidecar shape — Allure's `TestResultContainer` with `befores`, `afters` and a `children` uuid list — keeps the test result clean, stores a shared fixture exactly once, and lets each file be written independently as a run proceeds. Its costs: a `FixtureResult` has no `uuid`, no `historyId` and no `labels`, so a fixture cannot be trended, linked or addressed at all; and membership is a join that fails silently when a uuid does not resolve. The in-tree shape — ReportPortal's `TestItemTypeEnum`, where `BEFORE_METHOD` and its siblings are items like any other — gives every fixture an id, a parent, logs and a status, and needs one uniform traversal. Its cost: fixtures are in the same population as tests, so every consumer must exclude them, which is why the enum carries an `awareStatistics` flag per type.

go deeper

for a junior

Know the two placements exist: setup either sits in a separate container beside the results, or appears as its own typed node inside the run's tree.

for a middle

Name one concrete consequence of each: a fixture with no id cannot be trended, and a fixture node in the tree must be excluded from counts by its type.

for a senior

Predict the failure each shape produces in practice — a fixture that silently appears nowhere, or a pass rate diluted by setup nodes counted as tests.

for a principal

Decide deliberately when consolidating: what the primary consumer question is, whether fixtures must be addressable, how many consumers must honour the counting rule, and what a partial run should look like.

## The same problem, two answers Every result model has to reconcile one awkward fact: **setup and teardown are real executed work, but they are not tests.** They have a status, a duration, logs and often their own nested steps, yet nobody wants them in the answer to "how many tests ran?". Two shipped models solve it in opposite directions. | | sidecar container | typed item in the tree | |---|---|---| | where the fixture lives | a separate `-container.json`, in `befores` / `afters` | a node in the same tree as the cases | | how membership is expressed | a `children` list of uuids | parent/child position in the tree | | how it is told from a test | by which file and field it sits in | by its type, e.g. `BEFORE_METHOD` | | how counts avoid it | it was never in the case set | a per-type flag, `awareStatistics` | | identity of a fixture | none: no `uuid`, no `historyId`, no `labels` | a full item id like any other node | ## What the sidecar shape buys, and what it costs **Buys:** - A `-result.json` is complete on its own; a consumer that only wants case outcomes can ignore containers entirely. - A fixture shared by many cases is stored **once**, with one `start`/`stop` pair, so the fact that it ran once survives in the data. - Files are written independently as scopes close, so a killed run still leaves a coherent partial directory and parallel workers need no coordination. **Costs:** - **A fixture has no identity.** `FixtureResult` carries `name`, `status`, `statusDetails`, `stage`, `steps`, `attachments`, `parameters`, `start` and `stop` — and no key. You cannot trend one fixture across runs, attach a ticket to it, or reference it from anywhere. - **The join can silently vanish.** If `children` is empty or names uuids with no results, the fixture is on disk and in no report, with no error raised. - **Attribution looks like repetition.** One fixture projected onto twenty cases produces twenty identical panels, and any consumer that aggregates per case double-counts a single event. ## What the in-tree shape buys, and what it costs **Buys:** - **One traversal covers everything.** A consumer walks the tree once; there is no second file family and no join to get wrong. - **Fixtures are addressable.** They have ids, parents, statuses, logs and attachments, so a failing setup can be linked to, commented on, or analysed by exactly the machinery that handles cases. - **Ordering is structural.** Position in the tree already says what ran inside what, without leaning on timestamps. **Costs:** - **Fixtures share a population with tests.** Every count, ratio and comparison must exclude them, which is why the type enum ships the exclusion as data: only the structural types are statistics-aware, and all ten `BEFORE_*`/`AFTER_*` types are not. - **Every consumer must know the type vocabulary.** A tool that treats an unknown type as a test will silently inflate totals, and the failure mode is a plausible-looking number rather than an error. - **Nothing on a case node says what prepared it.** You have to walk up to find the fixtures that applied. ## How to choose, if you are the one designing the consolidation 1. **Ask what the primary question is.** If consumers mostly want per-case outcomes, the sidecar keeps the hot path clean. If they want to browse and act on a run as a whole, the tree is friendlier. 2. **Ask whether fixtures need to be addressable.** The moment someone wants to trend "how long our setup takes" or attach a ticket to a broken fixture, a shape with no identity for fixtures becomes the blocker, and adding one later is a format change. 3. **Ask how many independent consumers there will be.** Every extra consumer of the in-tree shape is another place that must exclude fixture types correctly. Encoding the exclusion in the type itself is what keeps that from being reimplemented and got wrong repeatedly. 4. **Ask what a partial run must look like.** Independently written files degrade gracefully; a tree assembled by a live service depends on the reporting session closing properly. ## The line worth remembering The sidecar removes fixtures from the population and pays for it with a fragile join and identity-less fixtures. The tree keeps fixtures in the population and pays for it with a flag every consumer must honour. Consolidating results from both means normalising to one of these choices deliberately — and whichever you pick, writing down which nodes are countable, because that decision does not survive a format conversion on its own.

  • Which shape would you pick if fixtures had to be trended across runs?
    The in-tree one, or an extension of the sidecar that gives fixtures a key. A `FixtureResult` has no id, no history key and no labels, so there is nothing to trend on; you would be reduced to matching on container name plus fixture name, which breaks on renames.
  • What breaks first when you convert one shape into the other?
    Counting. Flattening containers into a tree adds fixture nodes to the case population unless you carry an exclusion rule across; lifting typed fixture nodes into containers loses their ids and their tree position, so ordering has to be rebuilt from timestamps.

saying these in an interview costs you the question

  • Calls one shape simply better without naming a cost
  • Assumes fixtures always have an identity of their own
  • Forgets to exclude fixture nodes when counting tests
  • Thinks a broken membership join raises an error
  • Converts between shapes without carrying the counting rule