skip to content

What is a DisplayNameGenerator and how do you apply one to derive readable test names automatically?

level: middleimportance: should knowfreq 48%

answer

  1. Generator = strategy that derives names automatically
  2. @DisplayNameGeneration(X.class) applies it, incl. nested
  3. Built-ins: Standard, Simple, ReplaceUnderscores, IndicativeSentences
  4. Default via junit.jupiter.displayname.generator.default
  5. Explicit @DisplayName overrides the generator

basics

~10 s

A DisplayNameGenerator is a JUnit 5 strategy that builds a test's display name from its class/method automatically. You attach one with @DisplayNameGeneration so you do not have to write @DisplayName on every test.

solid answer

~50 s

A DisplayNameGenerator is a JUnit Jupiter interface whose implementations compute the display name for a class, a nested class, and a method, so readable names are derived systematically instead of being written by hand on each test. You opt in with @DisplayNameGeneration(SomeGenerator.class) on the class; it applies to that class and its @Nested classes. JUnit ships built-ins: Standard (the default — class/method names), Simple (drops empty parentheses), and ReplaceUnderscores (turns underscores into spaces, so order_is_preserved becomes "order is preserved"). There is also IndicativeSentences, which builds a sentence by joining the enclosing class name with the method name. You can implement the interface for custom rules — e.g. strip a leading "should" or camelCase-split. Precedence still favors an explicit @DisplayName: if a method has one, it wins over the generator. You can also set a project-wide default generator via the junit.jupiter.displayname.generator.default configuration parameter.

go deeper

for a junior

Knows generators exist and that ReplaceUnderscores turns underscores into spaces.

for a middle

Applies @DisplayNameGeneration, names the built-ins, and knows it propagates to nested classes.

for a senior

Configures a project-wide default generator, explains precedence vs. explicit names, and writes a custom generator when conventions demand it.

for a principal

Defines the team's naming strategy (generator + property vs. per-class annotations), balancing consistency, readability, and migration cost across modules.

## The two ways to get readable names JUnit 5 gives you two complementary mechanisms: 1. **`@DisplayName("...")`** — you *write* the name explicitly on each element. 2. **`DisplayNameGenerator`** — a *strategy* that *derives* the name automatically from the class/method, so you don't repeat yourself. A **`DisplayNameGenerator`** is an interface (`org.junit.jupiter.api.DisplayNameGenerator`) with methods to generate a name for: a top-level class, a `@Nested` class, and a method. An implementation encodes a naming convention. ## Built-in generators - **`Standard`** — the default. Uses the simple class name and the method name with its parameter list, e.g. `removesItem()`. - **`Simple`** — like Standard but removes the trailing `()` for methods that take no parameters, so `removesItem` instead of `removesItem()`. - **`ReplaceUnderscores`** — replaces each underscore in the identifier with a space. So a method named `removes_item_at_zero_quantity` displays as `removes item at zero quantity`. This is popular because you write readable intent in the *method name itself*. - **`IndicativeSentences`** — builds a sentence by concatenating the enclosing (class/`@Nested`) display name with the method's generated name, joined by a separator (default `, `). It internally delegates to another generator (by default `ReplaceUnderscores`) for the pieces. ## Applying a generator: `@DisplayNameGeneration` You attach a generator with the `@DisplayNameGeneration` annotation on a class. It applies to that class *and its nested classes*: ```java import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; @DisplayNameGeneration(ReplaceUnderscores.class) class OrderTest { @Test void rejects_a_negative_total() { /* shows as: "rejects a negative total" */ } } ``` ## Project-wide default Instead of annotating every class, set a configuration parameter so *all* tests use a generator by default: ``` junit.jupiter.displayname.generator.default = \ org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores ``` (put it in `junit-platform.properties` on the test classpath). An annotation on a specific class still overrides this default for that class. ## Precedence (important) The resolution order for a given element is: an explicit **`@DisplayName`** wins; otherwise the **`@DisplayNameGeneration`** on the (possibly enclosing) class; otherwise the **configured default**; otherwise **`Standard`**. So generators and explicit names coexist — you can let most names be derived and override the few that need special wording. ## Custom generators Implement the interface (or extend `DisplayNameGenerator.Standard`) to encode your own convention — e.g., split camelCase, drop a `should` prefix, or append the requirement id. You then point `@DisplayNameGeneration(MyGenerator.class)` (or the default property) at it. Key terms: a **`@Nested` class** is an inner test class used to group related tests; a **configuration parameter** is a key/value JUnit reads from `junit-platform.properties`, system properties, or the launcher.

  • If a method has both an explicit @DisplayName and the class uses ReplaceUnderscores, which name shows?
    The explicit @DisplayName wins; the generator is ignored for that method.
  • How do you make every test class in the project use ReplaceUnderscores without annotating each one?
    Set junit.jupiter.displayname.generator.default to the generator's fully qualified name in junit-platform.properties.

saying these in an interview costs you the question

  • Thinking you must choose either @DisplayName or a generator — they coexist with explicit names winning
  • Believing @DisplayNameGeneration must be repeated on each @Nested class (it inherits)
  • Confusing ReplaceUnderscores with Simple (underscores→spaces vs. dropping parentheses)
  • Assuming the generator can change which tests run

context