A parser reads the stack trace out of `<failure>` in a Maven Surefire JUnit-XML file by taking the element's text, then gets an empty string when it applies the same code to `<rerunFailure>`. What is different about how those two elements carry a trace?
answer
- not every outcome element has the same shape
- one is simple content, the other is not
- the repeated ones nest a required child
- descend one level for the trace
basics
~10 s<failure> and <error> are simple content: the trace is the element's own text. The four repetition elements are complex, and each requires a <stackTrace> child exactly once, so their trace sits one level deeper.
solid answer
~40 sIn `surefire-test-report.xsd`, `<failure>` and `<error>` are declared as **simpleContent** - they carry `@message` and `@type` as attributes and hold the stack trace as their own text node. The four repetition elements `<rerunFailure>`, `<flakyFailure>`, `<rerunError>` and `<flakyError>` are declared as **complexType**: each requires exactly one `<stackTrace>` child, and the trace lives in that child rather than in the parent. They also allow their own optional `<system-out>` and `<system-err>`, so each attempt can keep the console output it produced - something a `<failure>` has no room for - plus the same optional `@message` and `@type`. A parser taking the element's text therefore reads a trace from `<failure>` and reads whitespace from `<rerunFailure>`; it has to descend into `<stackTrace>` instead.
code
xml · 9 lines<testcase name="syncsCatalog" classname="com.example.SyncTest" time="1.80">
<failure message="expected 12 rows but was 0" type="java.lang.AssertionError">java.lang.AssertionError: expected 12 rows but was 0
at com.example.SyncTest.syncsCatalog(SyncTest.java:44)</failure>
<rerunFailure message="expected 12 rows but was 0" type="java.lang.AssertionError">
<stackTrace>java.lang.AssertionError: expected 12 rows but was 0
at com.example.SyncTest.syncsCatalog(SyncTest.java:44)</stackTrace>
<system-out>retrying against catalog-2</system-out>
</rerunFailure>
</testcase>go deeper
Know that the trace of a re-run attempt lives in a <stackTrace> child, while the trace of a plain <failure> is the element's own text.
Explain the simpleContent versus complexType distinction, and list what a repetition element may hold: one required <stackTrace>, an optional <system-out> and <system-err>, and optional @message and @type.
Be ready to say how you would find this in a consumer that silently reports empty traces for re-run attempts, and why validating the file against surefire-test-report.xsd would not have caught it.
Argue for or against a house rule that every consumer of these files be tested against a fixture containing all four repetition elements, not only <failure> and <error>.
The JUnit-XML file Maven Surefire writes looks uniform from a distance: a `<testsuite>` root, `<testcase>` children, and an outcome element inside each case that did not pass. It is not uniform. `surefire-test-report.xsd` declares the single-run outcome elements and the repetition elements with **two different content models**, and a parser written against one of them silently returns nothing for the other. ## Two content models in one schema `<failure>` and `<error>` are declared as **simpleContent**. That means the element may carry attributes and a text node, and nothing else. Its attributes are `@message` (the short failure message) and `@type` (the exception or assertion class), and the stack trace is the element's own text. The four repetition elements - `<rerunFailure>`, `<flakyFailure>`, `<rerunError>` and `<flakyError>` - are declared as **complexType**. Each one: - requires **exactly one `<stackTrace>` child**, which holds the trace text; - then allows an optional `<system-out>` and an optional `<system-err>`, in that order; - carries the same optional `@message` and `@type` attributes as its single-run counterpart; - is itself `minOccurs="0" maxOccurs="unbounded"`, so a case may carry several. So the trace of a re-run attempt sits **one level deeper** than the trace of an ordinary failure. Taking the text of a `<rerunFailure>` returns the whitespace between the tags, not the trace, and a parser that does not descend into `<stackTrace>` reports every re-run attempt as having no trace at all. Surefire's own writer says as much in a comment beside the branch that produces the two shapes: the structure is inconsistent because of a legacy design choice, and ideally every outcome element would be complex with its own trace child. It is not going to be unified retroactively, so consumers have to handle both. ## What the extra nesting buys The nesting is not gratuitous. Because a repetition element is complex, it has room for children a `<failure>` cannot have - and the useful ones are `<system-out>` and `<system-err>`. | | `<failure>` / `<error>` | the four repetition elements | |---|---|---| | content model | simpleContent | complexType | | stack trace | element text | required `<stackTrace>` child | | `@message`, `@type` | optional attributes | optional attributes | | own console output | not possible | optional `<system-out>`, `<system-err>` | | how many per `<testcase>` | `<failure>` unbounded, `<error>` at most once | all four unbounded | That last row of children is the practical payoff: a case re-run three times can keep the console output *each attempt* produced beside that attempt's own trace, rather than merging everything into one `<system-out>` on the `<testcase>`. When the first attempt hit a different host, a stale fixture, or a slower dependency than the run that finally passed, per-attempt output is often the only evidence in the file that says so. ## How the failure shows up in practice The bug this produces is quiet, because nothing errors: 1. A consumer is written and tested against files from builds that never re-ran anything. Those files contain only `<failure>` and `<error>`, and reading element text works. 2. Re-runs are switched on somewhere. The files now contain repetition elements too. 3. The consumer keeps parsing without complaint and starts showing re-run attempts with a blank trace, or with the message attribute standing in for one. 4. Validating the file against `surefire-test-report.xsd` does not catch it - the file is perfectly valid. It is the *reader* that is wrong. The fix is to branch on element name: read text for `<failure>` and `<error>`, and read the `<stackTrace>` child for the four repetition elements. Anything that wants to be generic can normalise both shapes into one internal record at the point of parsing, so nothing downstream has to know which element it came from. ## Details worth having exactly right - **`<stackTrace>` is required and singular.** One repetition element cannot carry two traces, and it cannot carry none. Several attempts mean several elements, not several `<stackTrace>` children inside one. - **`@message` and `@type` are optional on all of them.** Surefire adds `@message` only when the report entry actually has one, and derives `@type` from the trace, so an element that carries a trace and no attributes is well formed and normal. - **There is no `<message>` element.** The message is an attribute in every one of these elements; only the trace and the console output are children. - **Child order inside `<testcase>` is a sequence**, so the failure-flavoured elements come before the error-flavoured ones, and `<system-out>` and `<system-err>` on the case itself come last. A writer that emits them out of order produces a file that does not validate, even though most readers will not notice. The one-line version to keep in your head: in this schema, a trace lives in the element for a case that ran once, and in a child for an attempt of a case that ran more than once.
- The schema lets `<rerunFailure>` carry its own `<system-out>` and `<system-err>`, while `<failure>` cannot. What does that make possible?Per-attempt console output. A case re-run three times can keep the log each attempt produced next to that attempt's `<stackTrace>`, instead of one merged `<system-out>` on the `<testcase>`. That is often the only evidence in the file that a failed attempt hit a different host or a different fixture state than the run that finally passed.
- How many `<stackTrace>` children may one `<flakyFailure>` element have?Exactly one. It is required and it may not repeat. The `@message` and `@type` attributes beside it are optional, so a well-formed `<flakyFailure>` can carry a trace and nothing else - but it can never carry two traces, and several attempts mean several `<flakyFailure>` elements rather than several children inside one.
saying these in an interview costs you the question
- Assumes every outcome element carries its trace as text content
- Reads <rerunFailure> text and reports an empty stack trace
- Thinks <stackTrace> is optional inside a repetition element
- Believes <failure> can nest its own <system-out> child
- Expects a <message> child element rather than a @message attribute