skip to content

JUnit 5 from Kotlin

Using JUnit 5 directly from Kotlin brings backtick test names, @Nested grouping, and the PER_CLASS lifecycle that makes non-static setup possible. That lifecycle detail is the Kotlin-specific gotcha, since Kotlin has no statics.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

How do you write a JUnit 5 test method with a human-readable name in Kotlin, and why is this idiomatic?

level: juniorimportance: must knowfreq 70%

answer

  1. Backticks allow spaces in names
  2. @Test from org.junit.jupiter.api
  3. Method name becomes the display name
  4. @DisplayName for forbidden chars
  5. . ; [ ] / \ < > still illegal

basics

~10 s

In Kotlin you can name a function using backticks, so you write the test name as a normal sentence with spaces. JUnit then shows that readable name in the test report.

solid answer

~40 s

Kotlin lets you wrap a function name in backticks (`` `...` ``), which permits spaces and most punctuation. JUnit 5 picks up the method name as the default display name, so `` @Test fun `returns empty list when no items match`() `` reads like a spec sentence in the IDE and reports. You annotate the function with `@org.junit.jupiter.api.Test` from JUnit Jupiter. This avoids cramped camelCase names and is the standard Kotlin convention for test methods. JUnit 5 also offers `@DisplayName("...")` as an alternative when you need characters illegal even in backticks (the JVM forbids `. ; [ ] / \` plus `<` and `>` in identifiers), or want to keep a code-friendly function name. Backticks are preferred for plain sentences because they keep the name and the display in one place.

code

kotlin · 9 lines
kotlin
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

class DiscountTest {
    @Test
    fun `applies 10 percent off above threshold`() {
        assertEquals(90, discount(100))
    }
}

go deeper

for a junior

Knows backticks allow spaces and that @Test runs the method.

for a middle

Knows the name becomes the display name automatically and the correct Jupiter import.

for a senior

Knows the JVM character restrictions and when @DisplayName is required instead.

for a principal

Sets team conventions: backticks for tests only, naming-as-spec style, and a DisplayNameGenerator policy.

## The problem JVM test reports show method names. In Java you get cramped names like `returnsEmptyListWhenNoItemsMatch`. Kotlin solves this elegantly. ## Backtick-quoted identifiers Kotlin allows any function or property name to be wrapped in **backticks** (`` ` ``). Inside backticks you may use spaces and most punctuation: ```kotlin import org.junit.jupiter.api.Test import kotlin.test.assertEquals class CartTest { @Test fun `total is zero for an empty cart`() { assertEquals(0, Cart().total) } } ``` The annotation `@Test` comes from **`org.junit.jupiter.api.Test`** (JUnit 5 / Jupiter), not the older JUnit 4 `org.junit.Test`. ## Why JUnit shows the readable name JUnit 5's default `DisplayNameGenerator` uses the method name. Since Kotlin's backtick name *is* the method name at the bytecode level, the IDE and HTML/console reports print `total is zero for an empty cart` verbatim. No extra annotation needed. ## Limits of backticks The JVM identifier rules still forbid a few characters even inside backticks: `.`, `;`, `[`, `]`, `/`, `\`, and (on the JVM) `<` and `>`. If your sentence needs those, use the **`@DisplayName("...")`** annotation, whose string is unrestricted: ```kotlin @Test @DisplayName("GET /users returns 200") fun getUsersReturns200() { /* ... */ } ``` ## Convention Backtick names are the de-facto Kotlin standard for tests and are recommended in Kotlin coding conventions for test code specifically (not production code). Use them to express behaviour as a sentence.

  • Which characters are still illegal inside a backtick name?
    JVM identifier rules forbid `.`, `;`, `[`, `]`, `/`, `\`, and on the JVM also `<` and `>`. For those, use @DisplayName.
  • Should you use backtick names for production code too?
    No. Kotlin conventions reserve them for tests; calling such functions from other code requires backticks at every call site, which is ugly.

Backtick names are like writing the test's headline directly in the function name instead of hiding it in a comment.

saying these in an interview costs you the question

  • Thinking backticks are a JUnit feature rather than a Kotlin language feature
  • Importing org.junit.Test (JUnit 4) instead of org.junit.jupiter.api.Test
  • Claiming any character at all is allowed inside backticks
  • Believing you need @DisplayName for every readable test name

context

open as a page

Why does @BeforeAll often fail in a Kotlin JUnit 5 test, and how does @TestInstance(PER_CLASS) fix it?

level: middleimportance: must knowfreq 65%

basics

~10 s

By default JUnit needs @BeforeAll to be static, but Kotlin classes don't have plain static methods. Adding @TestInstance(PER_CLASS) lets JUnit reuse one instance so a normal (non-static) @BeforeAll works.

open as a page

How do you use @Nested in Kotlin to group JUnit 5 tests, and what lifecycle behaviour applies to nested classes?

level: middleimportance: should knowfreq 50%

basics

~10 s

You put related tests inside an inner class marked @Nested. JUnit runs them as a sub-group, and the inner class can access the outer class's fields, which helps share setup.

open as a page

Compare the two ways to run a once-per-class @BeforeAll in a Kotlin JUnit 5 test, and explain when each is appropriate.

level: seniorimportance: should knowfreq 40%

basics

~10 s

You can either put @BeforeAll in a companion object with @JvmStatic, making it truly static, or add @TestInstance(PER_CLASS) so a normal method works. Static suits truly shared resources; PER_CLASS suits instance-bound setup.

open as a page

A Kotlin test class uses @TestInstance(PER_CLASS) and passes locally but fails intermittently in CI. What are the likely causes and how do you diagnose them?

level: seniorimportance: nice to knowfreq 28%

basics

~10 s

PER_CLASS reuses one instance, so leftover state from one test can affect another. If tests run in a different order in CI, hidden dependencies surface. Reset shared state and pin or remove order assumptions.

open as a page