skip to content

Invocation Display Names

Naming each parameterized invocation so failures are readable at a glance. Small feature, common question: the placeholder syntax and the argumentsWithNames default.

on this pageshow

questions

3

In JUnit 5, a data-driven test method runs once per set of arguments and each run appears as its own entry in the IDE tree and the XML report. How do you control the text shown for each of those per-run entries, and what does that text look like if you do nothing?

level: juniorimportance: must knowfreq 45%

answer

  1. name attribute = pattern string
  2. {index} is 1-based, {0} is first arg
  3. default: [{index}] {argumentsWithNames}
  4. {displayName} = method, not invocation
  5. MessageFormat: double the apostrophe

basics

~10 s

Set the name attribute of @ParameterizedTest, e.g. @ParameterizedTest(name = "[{index}] input={0}"). JUnit fills placeholders per invocation: {index} (1-based), {arguments}, {argumentsWithNames}, {displayName}, and positional {0}, {1}. Default pattern is "[{index}] {argumentsWithNames}".

solid answer

~50 s

Each invocation of a `@ParameterizedTest` is its own node in the JUnit test tree, and its label comes from the annotation's `name` attribute — a pattern string JUnit fills in per invocation. Placeholders: - `{index}` — invocation number, starting at **1** - `{arguments}` — all arguments, comma-separated, via `toString()` - `{argumentsWithNames}` — the same, as `param=value` pairs - `{displayName}` — display name of the test **method** itself - `{0}`, `{1}`, … — one argument by position So `@ParameterizedTest(name = "[{index}] {0} -> {1}")` renders as `[1] level -> true`. If you set nothing, JUnit uses `ParameterizedTest.DEFAULT_DISPLAY_NAME` = `"[{index}] {argumentsWithNames}"` (it was `{arguments}` before JUnit 5.8). The method's own `@DisplayName` is not part of the invocation label — it names the parent container — which is why teams add `{displayName}` explicitly when report lines get read out of context.

code

java · 13 lines
java
@DisplayName("palindrome detection")
@ParameterizedTest(name = "[{index}] {0} -> {1}")
@CsvSource({"level, true", "claude, false"})
void detects(String candidate, boolean expected) {
    assertEquals(expected, Palindromes.isPalindrome(candidate));
}
// [1] level -> true
// [2] claude -> false

@ParameterizedTest // default pattern
@ValueSource(strings = {"level"})
void defaultName(String candidate) { }
// [1] candidate=level

go deeper

for a junior

Know that @ParameterizedTest(name = ...) exists, name the common placeholders, and say the default is [{index}] {argumentsWithNames}.

for a middle

Add the mechanics: 1-based index, MessageFormat positional {0}, unknown tokens printed verbatim, and that the default changed in 5.8.

for a senior

Frame it as CI triage: the invocation label is the only per-case identity a failure line carries, so name the behaviour plus the discriminating input rather than dumping arguments.

for a principal

Talk about a suite-wide convention — a consistent, greppable invocation-name format so report tooling and flaky-test dashboards can key on it, rather than each author inventing a pattern.

## What is being named A `@ParameterizedTest` method is not one test at runtime. Jupiter turns the method into a **container** node, and every argument set becomes a **child invocation** node underneath it. The container carries the method's `@DisplayName` (or the method name); each child carries an *invocation display name*, computed from the pattern in the annotation's `name` attribute. Everything downstream — the IDE tree, the JUnit XML consumed by CI, HTML reports, `TestExecutionListener` output — reads that child label. It is the only place per-invocation identity is visible, so a bad pattern means a failure line that says `[7]` and nothing else. ## The placeholders The supported tokens, all defined as constants on `ParameterizedTest`: - `{displayName}` (`DISPLAY_NAME_PLACEHOLDER`) — the method's display name. - `{index}` (`INDEX_PLACEHOLDER`) — the invocation number, **1-based**, not 0-based. - `{arguments}` (`ARGUMENTS_PLACEHOLDER`) — all arguments joined with `, `, each rendered with `String.valueOf`. - `{argumentsWithNames}` (`ARGUMENTS_WITH_NAMES_PLACEHOLDER`) — the same list, each entry prefixed with the formal parameter name, e.g. `candidate=level, expected=true`. - `{0}`, `{1}`, … — positional access to a single argument. A pattern is free text with tokens embedded: `name = "#{index}: isPalindrome({0}) == {1}"`. ## The default `ParameterizedTest.DEFAULT_DISPLAY_NAME` is `"[{index}] {argumentsWithNames}"`. In JUnit 5.7 and earlier the default was `"[{index}] {arguments}"`; 5.8 switched it to the named form, which is why the same test suddenly reports `[1] candidate=level` after an upgrade. Nothing about the default includes `{displayName}` — inside an IDE that is fine because the parent node is right above, but a flat CI log line benefits from spelling it out. ## Mechanics worth knowing Under the hood the resolved pattern is run through `java.text.MessageFormat`. Two consequences follow. First, positional `{0}` works because MessageFormat owns numeric arguments. Second, MessageFormat treats the single quote as an escape character, so a literal apostrophe in the pattern must be doubled (`"can''t parse {0}"`), otherwise the surrounding text is silently swallowed. An unknown token such as `{idx}` is not an error — it is simply left in the output verbatim, which is the usual explanation for a report line reading `{idx} = 3`. Conversely, setting `name = ""` is a hard configuration error: JUnit requires a non-blank pattern and fails the test with a `PreconditionViolationException` rather than falling back to the default. `{argumentsWithNames}` needs real parameter names to be useful. Java only keeps them in the class file when compiled with the `-parameters` flag; without it you get `arg0=level, arg1=true`. Test frameworks and Spring Boot's default build setup usually enable the flag, but a plain compile does not. ## Why interviewers ask The question separates people who have only run a parameterized test from people who have had to debug one from a CI report. When twenty invocations run and number 13 fails, a name like `[13] userId=null, expected=REJECTED` is the whole diagnosis; `[13]` costs a local re-run. Good practice is to name the *behaviour plus the discriminating input*, not to dump every argument — a rule that matters most when arguments are large objects whose `toString()` is a wall of text.

  • Is {index} zero-based or one-based, and how does it differ from {0}?
    {index} is one-based: the first invocation renders as 1. {0} is unrelated to the index — it is MessageFormat positional access to the first *argument* of that invocation. Writing name = "{0}" expecting an invocation counter is a classic mix-up; you get the argument value instead.
  • What happens if you write a placeholder JUnit does not recognise, or leave the name attribute empty?
    An unrecognised token like {idx} is left in the output literally, so the report shows the braces verbatim — no exception. An empty or blank name is different: JUnit validates it and fails the test with a configuration/precondition error instead of falling back to the default pattern.

saying these in an interview costs you the question

  • Thinking {index} starts at 0.
  • Believing {0} means the invocation number rather than the first argument.
  • Assuming the method's @DisplayName is automatically part of each invocation label.
  • Expecting a typo'd placeholder to throw, when it is simply printed verbatim.
  • Claiming name = "" restores the default pattern (it is a configuration error).

context

open as a page

A JUnit 5 parameterized test reports invocations as "[1] arg0=admin, arg1=403" instead of using the real parameter names. Explain what the {arguments} and {argumentsWithNames} placeholders each produce and why the names degraded to arg0/arg1.

level: middleimportance: should knowfreq 35%

basics

~20 s

{arguments} prints the argument values comma-separated via toString. {argumentsWithNames} prefixes each with its formal parameter name. Parameter names only survive in the class file when compiled with the -parameters flag; without it reflection reports arg0, arg1, so the label degrades.

open as a page

A large JUnit 5 codebase has hundreds of data-driven test methods and the team wants every one of them to report invocations in the same custom format, without editing every annotation. How would you do that, and which setting wins if a method also declares its own format?

level: seniorimportance: should knowfreq 28%

basics

~20 s

Set the JUnit Platform configuration parameter junit.jupiter.params.displayname.default (in junit-platform.properties on the test classpath, or as a system property) to the desired pattern. Precedence: an explicit name attribute on @ParameterizedTest wins, then the configuration parameter, then JUnit's built-in "[{index}] {argumentsWithNames}".

open as a page