skip to content

What does the @Test annotation do in JUnit 5, and what are the signature requirements for a method marked with it?

level: juniorimportance: must knowfreq 85%

answer

  1. org.junit.jupiter.api.Test (not org.junit)
  2. void return, no parameters by default
  3. not private/static/abstract; need not be public
  4. engine discovers via reflection, new instance per test
  5. no attributes — assertThrows/assertTimeout instead

basics

~10 s

@Test marks a method as a test case so JUnit runs it. The method must return void and take no parameters (unless something is injected), and should not be static or private.

solid answer

~40 s

@Test (from org.junit.jupiter.api) tells the JUnit 5 test engine that a method is a test case to discover and execute. The method must return void and is not allowed to be static, private, or abstract; JUnit instantiates the test class and invokes the method via reflection. It takes no parameters by default, though the Jupiter ParameterResolver mechanism can inject things like TestInfo or TestReporter. Each test typically runs on a fresh instance of the test class (per-method lifecycle), so tests stay isolated. You assert outcomes using static assertions (assertEquals, assertTrue, assertThrows). Unlike JUnit 4, the annotation lives in the org.junit.jupiter.api package, not org.junit, which is a common migration gotcha.

go deeper

for a junior

Knows @Test marks a runnable test, the method returns void and takes no args, and uses the org.junit.jupiter.api import.

for a middle

Explains discovery via reflection, the not-private/static/abstract rule, that public is not required, and that JUnit 4's expected/timeout attributes are gone.

for a senior

Connects @Test to the Platform/Engine/API split, the per-method instance lifecycle for isolation, and parameter injection via ParameterResolver.

for a principal

Can reason about extension-model implications (custom ParameterResolvers, lifecycle choices), migration strategy from JUnit 4, and why a no-attribute annotation pushes behavior into composable assertions/extensions.

## What a test framework is A **unit test** is a small piece of code that runs part of your program and checks it behaves as expected. A **testing framework** (here **JUnit 5**, the de-facto Java standard) is a library that *finds* your test methods, *runs* them, and *reports* pass/fail. You write the checks; the framework does the plumbing of discovery, execution, and reporting. ## What `@Test` is An **annotation** in Java is metadata you attach to code (with the `@` syntax) that tools can read via **reflection** (inspecting code structure at runtime). `@Test` is an annotation that means *"this method is a test case — run it."* In JUnit 5 (also called **JUnit Jupiter**) the annotation is: ```java import org.junit.jupiter.api.Test; ``` Note the package: **`org.junit.jupiter.api.Test`**. In the older JUnit 4 it was `org.junit.Test`. Importing the wrong one is a frequent mistake — the test silently isn't discovered. ## How JUnit finds and runs a `@Test` method JUnit 5 splits into the **Platform** (the launcher), an **Engine** (Jupiter is the engine for the new style), and the **API** (the annotations you write against). At run time the Jupiter engine: 1. Scans classes for methods annotated `@Test`. 2. Creates an instance of the test class (by default a **new instance per test method** — the `PER_METHOD` lifecycle — so one test can't leak state into another). 3. Runs any `@BeforeEach` setup, then invokes the `@Test` method via reflection. 4. Records a pass if the method returns normally, or a fail if an assertion fails or an exception is thrown. ## Signature requirements A `@Test` method must: - **Return `void`.** A non-void return type makes JUnit ignore or warn about the method — there is nothing meaningful to do with a return value. - **Not be `private`, `static`, or `abstract`.** It must be an ordinary instance method JUnit can call. (It does *not* need to be `public` in JUnit 5 — package-private is fine, unlike JUnit 4 which required `public`.) - **Take no parameters by default.** However, JUnit 5's **`ParameterResolver`** mechanism can *inject* arguments — e.g. `TestInfo`, `TestReporter`, or values supplied by extensions like `@ExtendWith(MockitoExtension.class)`. So "no parameters" really means "no parameters unless a resolver provides them." ## A minimal example ```java import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class CalculatorTest { @Test void addsTwoNumbers() { assertEquals(4, 2 + 2); } } ``` ## Key contrast with JUnit 4 In JUnit 4 you wrote `@Test(expected = ..., timeout = ...)`. JUnit 5 **dropped those attributes**; you now use `assertThrows` and `assertTimeout`/`assertTimeoutPreemptively` instead (covered in a separate question). The `@Test` annotation in Jupiter has **no elements** — `@Test` on its own, nothing in the parentheses.

  • Why does JUnit create a new instance of the test class for each @Test method by default?
    To isolate tests: a fresh instance means instance fields reset between tests, so one test cannot accidentally depend on or corrupt state left by another. This per-method lifecycle (TestInstance.Lifecycle.PER_METHOD) is the default; PER_CLASS reuses one instance.
  • Does a @Test method have to be public in JUnit 5?
    No. JUnit 5 only requires it not be private, static, or abstract. Package-private (default) visibility is conventional. JUnit 4 required public; that requirement was dropped.

saying these in an interview costs you the question

  • Saying the method must be public — JUnit 5 only forbids private/static/abstract
  • Using @Test(expected=...) or @Test(timeout=...) — those are JUnit 4, removed in 5
  • Importing org.junit.Test (JUnit 4) in a Jupiter test and wondering why it isn't run
  • Claiming a @Test method can return a value used by JUnit

context