skip to content

@Test

@Test marks a method the Jupiter engine will discover and run — no return value, and no parameters unless something injects them. Interviewers note that JUnit 5 dropped the expected and timeout attributes in favor of assertThrows and assertTimeout.

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

questions

5

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

open as a page

JUnit 4 let you write @Test(expected = SomeException.class) and @Test(timeout = 1000). How do you express those in JUnit 5, and why was the change made?

level: middleimportance: must knowfreq 70%

basics

~10 s

JUnit 5's @Test has no expected or timeout attributes. Instead you use assertThrows to check an exception is thrown and assertTimeout (or @Timeout) to bound how long code may run.

open as a page

A developer writes a method marked @Test but it never runs — no pass, no fail, no error. What are the common reasons a @Test method is silently not discovered or executed by JUnit 5?

level: middleimportance: should knowfreq 55%

basics

~20 s

Usual causes: importing JUnit 4's org.junit.Test instead of org.junit.jupiter.api.Test, the method being private or static, the test class or method matching no naming/include pattern in the build config, or the JUnit Platform/Jupiter engine not being on the runtime classpath.

open as a page

How does the JUnit 5 Jupiter engine discover and execute methods annotated with @Test, and where does @Test fit within the JUnit 5 architecture?

level: seniorimportance: should knowfreq 45%

basics

~20 s

JUnit 5 has a Platform that launches tests, a Jupiter engine that finds @Test methods by scanning classes via reflection, and the API (@Test) you annotate with. The engine builds a test plan, creates an instance per test, runs lifecycle callbacks, then invokes each @Test method.

open as a page

Why does JUnit 5's @Test have no attributes at all, and how does that decision interact with composed (meta-)annotations and the extension model?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

JUnit 5 keeps @Test a bare marker and moves behavior (exceptions, timeouts, conditions) into separate assertions, annotations, and extensions. That makes @Test composable: you can build your own annotations that combine @Test with tags or extensions, and add cross-cutting behavior without changing @Test itself.

open as a page