skip to content

JUnit

The default unit-testing framework on the JVM: annotations, assertions, lifecycle, parameterized tests, and the Jupiter extension model. Interviewers ask because nearly every Java or Kotlin codebase runs on it, and your habits here show up in every test you write.

on this pageshow

explore

questions

191 · 9 sections

In JUnit 5, some teams mark tests with one custom annotation such as @SlowIntegrationTest instead of writing @Test plus @Tag("slow") on every method. What is that custom annotation called, and how does JUnit know to treat the method as a test?

level: juniorimportance: must knowfreq 45%
basics
~20 s

It is a composed annotation: your own annotation type that is itself annotated with JUnit's @Test, @Tag, @ExtendWith and so on. Jupiter looks up annotations recursively, so anything meta-annotated with @Test is discovered and run as a test.

open as a page

In JUnit 5, what does the @Disabled annotation do, how does putting it on a test class differ from putting it on a single test method, and what is the string argument used for?

level: juniorimportance: must knowfreq 50%
basics
~20 s

@Disabled skips a test instead of running it, reporting it as skipped rather than failed. On a method it skips that method; on a class it skips every test in the class, including its nested classes. The string argument is the reason, shown in reports and IDEs.

open as a page

What rules must a Java method (and its enclosing class) satisfy for the JUnit 5 Jupiter engine to discover and run it as a test?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Annotate it with org.junit.jupiter.api.Test. The method must not be private, static or abstract, and must return void. Parameters are allowed only if a ParameterResolver supplies them (TestInfo, TestReporter, @TempDir...). The class must not be abstract and needs a single constructor; neither class nor method has to be public.

open as a page

In JUnit 5, how do you make a single test fail automatically if it runs longer than a chosen duration, and what exactly happens when that limit is reached?

level: juniorimportance: must knowfreq 40%
basics
~20 s

Annotate the test with JUnit Jupiter's @Timeout, e.g. @Timeout(value = 500, unit = TimeUnit.MILLISECONDS). The default unit is seconds. When the limit elapses the test fails with a TimeoutException naming the method and the configured duration.

open as a page

You wrote a custom JUnit 5 annotation that bundles @Test and @Tag, but methods marked with it are no longer picked up as tests. Which Java meta-annotations must your annotation type itself declare, and what goes wrong when each is missing or wrong?

level: middleimportance: must knowfreq 38%
basics
~20 s

@Retention(RetentionPolicy.RUNTIME) is mandatory — the default CLASS retention hides the annotation from reflection, so JUnit never sees it. @Target must include the element you annotate (METHOD for a test marker, TYPE for class-level, ANNOTATION_TYPE to allow further composition).

open as a page

In JUnit 5, how do you make a test stop at runtime and report itself as skipped when a precondition is not satisfied, and how does that outcome differ from a failure?

level: juniorimportance: must knowfreq 45%
basics
~20 s

Call Assumptions.assumeTrue(condition) (or assumeFalse) at the top of the test. If the condition does not hold, a TestAbortedException is thrown, the rest of the method never runs, and the test is reported as aborted or skipped — the build stays green rather than red.

open as a page

In JUnit 5, what is the difference between assertEquals and assertSame, and when would you deliberately reach for assertSame?

level: juniorimportance: must knowfreq 72%
basics
~20 s

assertEquals compares with equals() — value equality. assertSame compares with == — the same object in memory. Use assertSame only when identity is the contract you are testing: caches, interned or flyweight instances, singletons, or a method that returns this.

open as a page

How do you assert in JUnit 5 that a piece of code throws a particular exception, and how do you then verify details of that exception such as its message or cause?

level: juniorimportance: must knowfreq 78%
basics
~20 s

Call assertThrows(ExpectedType.class, () -> codeUnderTest()). It fails if nothing is thrown or if the thrown type does not match, and on success it returns the thrown exception, typed, so you can go on asserting its message, cause or custom fields.

open as a page

A JUnit 5 test checks five properties of one returned object; the first failing check ends the test and the other four never run, so each fix only reveals the next problem. What does org.junit.jupiter.api.Assertions.assertAll give you here, and how do you use it?

level: juniorimportance: must knowfreq 58%
basics
~20 s

assertAll takes lambdas, runs every one of them even after some fail, then reports all failures together in one aggregated error. Use it for independent checks on the same object so a single run shows every problem instead of only the first.

open as a page

In JUnit 5 (Jupiter), how do you assert inside a test that a block of code finishes within a given time budget, and what does the framework report when the block is too slow?

level: juniorimportance: must knowfreq 45%
basics
~20 s

Use Assertions.assertTimeout(Duration.ofMillis(500), () -> code). It runs the block on the test thread, then fails if it took longer, reporting the expected budget and how much it overran. A supplier form returns the block's value.

open as a page

In JUnit 5, describe when the methods annotated @BeforeAll, @BeforeEach, @AfterEach and @AfterAll run relative to the test methods of a class, and what each is typically used for.

level: juniorimportance: must knowfreq 72%
basics
~20 s

@BeforeAll runs once before any test in the class; @AfterAll runs once after the last one. @BeforeEach runs before every individual test and @AfterEach after every one, so for three tests you get one BeforeAll, three BeforeEach/test/AfterEach cycles, then one AfterAll.

open as a page

In JUnit 5 (Jupiter), if a test class has three @Test methods, how many instances of that class does the framework create by default, and what is the reason for that design?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Three. Jupiter's default lifecycle is PER_METHOD: a brand-new instance of the test class is constructed before each @Test method. Instance fields therefore start fresh every time, so one test cannot leak state into another through fields.

open as a page

In JUnit 5 (Jupiter), what order are the @Test methods inside one test class executed in when you do not configure anything, and what does that imply for how those tests must be written?

level: juniorimportance: must knowfreq 58%
basics
~20 s

Jupiter applies no orderer by default, so methods run in a deterministic but intentionally non-obvious order — not source order, not alphabetical — and it may change between versions. Each test must therefore pass independently, in any order.

open as a page

Why does JUnit 5 normally require @BeforeAll and @AfterAll methods to be declared static, and what happens if you leave the keyword off?

level: middleimportance: must knowfreq 52%
basics
~20 s

By default JUnit creates a new test-class instance for every test method, so there is no single instance a class-level hook could belong to; it must be static to be callable once per class. Without static you get a runtime JUnitException telling you the method must be static, not a compile error.

open as a page

What changes in a JUnit 5 test class when you annotate it with @TestInstance(TestInstance.Lifecycle.PER_CLASS), and why would you want that?

level: middleimportance: must knowfreq 58%
basics
~20 s

Jupiter creates one instance of the class for all its test methods instead of one per method. Instance fields then survive between tests, and @BeforeAll/@AfterAll (and non-static @MethodSource factory methods) may be non-static because there is now an instance to own them.

open as a page

In JUnit 5, how does the @CsvSource annotation supply arguments to a parameterized test method, and how does each row map to that method's parameters?

level: juniorimportance: must knowfreq 62%
basics
~20 s

@CsvSource sits next to @ParameterizedTest and holds an array of comma-separated row strings. JUnit runs the test method once per row, splitting the row on commas and passing the pieces, left to right, as the method's parameters.

open as a page

In JUnit 5, how does the @MethodSource annotation supply arguments to a parameterized test, and what happens if you leave the annotation's value empty?

level: juniorimportance: must knowfreq 58%
basics
~20 s

@MethodSource names a factory method that returns a Stream (or Iterable/array) of arguments; JUnit runs the test once per element. If you leave the value empty, JUnit looks for a factory method with the same name as the test method.

open as a page

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%
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}".

open as a page

You want a JUnit 5 test method to run once for each of the literal inputs 1, 2 and 3 without writing a loop inside the test. Which annotations do you use, and what are the limitations of that argument source?

level: juniorimportance: must knowfreq 60%
basics
~20 s

Mark the method @ParameterizedTest and add @ValueSource(ints = {1, 2, 3}); it runs once per value, passing one argument each time. Limits: exactly one array attribute, only primitives plus String and Class, values must be compile-time constants, and null cannot be supplied.

open as a page

A JUnit 5 parameterized test is fed rows of twelve columns and you don't want a twelve-parameter test method. What does declaring a parameter of type ArgumentsAccessor give you, and how do you read values out of it?

level: middleimportance: must knowfreq 45%
basics
~20 s

Declare one parameter of type ArgumentsAccessor. JUnit passes it the whole argument array for that invocation instead of consuming a single column. You read values positionally: getString(0), getInteger(3), get(5, LocalDate.class), plus size(), toList() and toArray().

open as a page

In JUnit 5 (Jupiter), how do you plug a reusable extension such as the Mockito or Spring test integration into a test, and which declaration sites are valid for the @ExtendWith annotation?

level: juniorimportance: must knowfreq 60%
basics
~20 s

Declaratively, with @ExtendWith(SomeExtension.class). It can go on a test class, on a single test method, on a field or parameter, or on your own meta-annotation. JUnit builds the extension with its no-arg constructor, and class-level registration is inherited by subclasses and @Nested classes.

open as a page

In JUnit 5, how do you obtain a throwaway directory for a test that needs to read and write real files, and what field or parameter types can receive it?

level: juniorimportance: must knowfreq 55%
basics
~20 s

Annotate a field or a method parameter of type java.nio.file.Path or java.io.File with JUnit Jupiter's @TempDir. Jupiter's built-in extension creates a fresh empty directory in the OS temp location before use and recursively deletes it, with its contents, afterwards.

open as a page

JUnit 5's extension model defines BeforeAllCallback, BeforeEachCallback, BeforeTestExecutionCallback and their After* counterparts. In what order do these run around a single test method, and how does that order change when several extensions are registered?

level: middleimportance: must knowfreq 45%
basics
~10 s

Order is BeforeAllCallback, @BeforeAll, BeforeEachCallback, @BeforeEach, BeforeTestExecutionCallback, the test, AfterTestExecutionCallback, @AfterEach, AfterEachCallback, @AfterAll, AfterAllCallback. Multiple extensions nest like an onion: Before* in registration order, After* in reverse.

open as a page

In JUnit 5, how do you make a test class or a test method skip itself automatically based on a runtime check — for example, a required database not being reachable — using the extension model rather than commenting the test out?

level: middleimportance: must knowfreq 50%
basics
~20 s

Implement JUnit 5's ExecutionCondition extension. Its evaluateExecutionCondition(ExtensionContext) returns ConditionEvaluationResult.enabled(reason) or disabled(reason). JUnit calls it for every container and test it is registered on; a disabled result skips that node, reported as skipped rather than failed.

open as a page

How do you make JUnit 5 hand your test methods a ready-made collaborator — say a pre-configured HTTP client — as a method argument? Walk through the extension interface, its two methods, and how you keep it from claiming parameters it should not.

level: middleimportance: must knowfreq 45%
basics
~20 s

Implement ParameterResolver: supportsParameter(ParameterContext, ExtensionContext) returns true only for parameters you own, and resolveParameter(...) returns the object. Register it with @ExtendWith. Narrow supportsParameter by both a custom annotation and the parameter type so it never competes with another resolver.

open as a page

When moving a test class from JUnit 4 to JUnit 5, what are the JUnit 5 equivalents of @Before, @BeforeClass, @After, @AfterClass, @Ignore and @Category, and what changes besides the names?

level: juniorimportance: must knowfreq 58%
basics
~10 s

@Before becomes @BeforeEach, @BeforeClass becomes @BeforeAll, @After becomes @AfterEach, @AfterClass becomes @AfterAll, @Ignore becomes @Disabled, @Category(X.class) becomes @Tag("x"). Packages change to org.junit.jupiter.api, assertions move to Assertions, and the message argument moves to last.

open as a page

In JUnit 4, what is a `@Rule`, and what can it do that plain `@Before` and `@After` methods cannot?

level: juniorimportance: must knowfreq 52%
basics
~20 s

A @Rule is a public, non-static field holding an object that wraps each test method. Because it wraps the call, it can run code before and after the test and also catch failures, retry, time out or skip it. @Before/@After only run beside the test.

open as a page

What does JUnit 4's `@RunWith` annotation do, and what executes a test class that carries no `@RunWith` at all?

level: juniorimportance: must knowfreq 46%
basics
~20 s

@RunWith(X.class) names the Runner class that takes over running that test class. Without it, JUnit falls back to its default runner — org.junit.runners.JUnit4, a subclass of BlockJUnit4ClassRunner — which finds the @Test methods, creates a new instance per method and applies @Before/@After and rules.

open as a page

The JUnit Platform can execute legacy JUnit 4 tests through an engine named junit-vintage-engine. Explain what that engine actually is, what kinds of test classes it accepts, and what it does NOT change about how those tests behave.

level: juniorimportance: must knowfreq 45%
basics
~20 s

junit-vintage-engine is a TestEngine implementation for the JUnit Platform. It discovers JUnit 3 (junit.framework.TestCase) and JUnit 4 classes, delegates execution to JUnit 4's own runners, and reports results back to the platform. The tests keep pure JUnit 4 semantics and gain no Jupiter features.

open as a page

JUnit 4 offered @Test(expected = SomeException.class) and the ExpectedException rule for exception tests. How do you express the same intent in JUnit 5, and why is the replacement considered stronger?

level: middleimportance: must knowfreq 52%
basics
~20 s

Use Assertions.assertThrows(SomeException.class, () -> code()), which returns the thrown exception so you can assert its message or cause. It scopes the expectation to one statement, unlike @Test(expected=), which passes if any line in the method throws that type.

open as a page

In JUnit 5, what is a DynamicTest object, how do you construct one, and how does it differ at runtime from a plain method annotated with @Test?

level: juniorimportance: must knowfreq 36%
basics
~20 s

A DynamicTest is a test case created at runtime as an object, via DynamicTest.dynamicTest(displayName, executable) — a display-name string plus a lambda holding the assertions. Unlike a @Test method it is not discovered by annotation scanning and carries no annotations of its own.

open as a page

In JUnit 5, what does annotating an inner test class with @Nested do, and why must that class be non-static?

level: juniorimportance: must knowfreq 50%
basics
~20 s

@Nested makes an inner class a child test container: its tests run under the outer class and reuse the outer class's fields and @BeforeEach setup. It must be non-static so every nested instance is bound to a live enclosing instance.

open as a page

A developer reports that in JUnit 5 their @BeforeEach method runs only once even though the factory method they wrote produced twenty executable test cases at runtime. Why does that happen, and how do you give each generated case its own fresh setup?

level: middleimportance: must knowfreq 32%
basics
~20 s

Dynamic tests are generated at runtime, so Jupiter's per-test callbacks bind to the generating method, not to each generated case: @BeforeEach and @AfterEach run once around the whole factory. Put setup and teardown inside each executable, typically via a shared wrapper helper.

open as a page

For a test method declared inside a JUnit 5 @Nested class, in what order do the enclosing and nested lifecycle callbacks run, how many objects does the framework create, and what does it take to declare @BeforeAll inside that nested class?

level: middleimportance: must knowfreq 40%
basics
~20 s

@BeforeEach runs outside-in (outer then nested), @AfterEach inside-out. With the default per-method lifecycle each test gets a fresh outer instance plus a fresh nested instance. @BeforeAll must be static, which needs Java 16+ or @TestInstance(PER_CLASS) on the nested class.

open as a page

What is a @TestFactory method in JUnit 5, and how does its execution differ from a plain @Test method?

level: middleimportance: must knowfreq 35%
basics
~20 s

A @TestFactory method is not a test; it is a factory invoked at runtime that returns dynamic test nodes, which JUnit then executes. Cases are produced during execution rather than fixed at discovery, so the number and names of tests can depend on runtime data.

open as a page

JUnit 5 ships annotations that switch a test off depending on the environment it runs in — for example @EnabledOnOs and @DisabledOnJre. Name the main members of that family, describe how they can be applied, and explain what shows up in the report for a test they switch off.

level: juniorimportance: must knowfreq 48%
basics
~20 s

JUnit 5 has @EnabledOnOs/@DisabledOnOs, @EnabledOnJre/@DisabledOnJre and @EnabledForJreRange/@DisabledForJreRange, plus @EnabledIfSystemProperty/@EnabledIfEnvironmentVariable and method-based @EnabledIf/@DisabledIf. They go on a test method or a whole class, and a switched-off test is reported as skipped with a reason — it never runs.

open as a page

In JUnit 5, what do you have to configure to make tests actually run in parallel, and where do those settings live?

level: juniorimportance: must knowfreq 45%
basics
~10 s

Parallel execution is off by default. Set junit.jupiter.execution.parallel.enabled=true, then set the default execution modes: junit.jupiter.execution.parallel.mode.default and junit.jupiter.execution.parallel.mode.classes.default to concurrent. Put them in junit-platform.properties on the test classpath, or pass them as JVM system properties.

open as a page

In JUnit 5, how do you label a subset of tests with the @Tag annotation so that a build can run only that subset, and what does @Tag actually do at discovery time?

level: juniorimportance: must knowfreq 50%
basics
~20 s

@Tag("slow") labels a test class or method; it is metadata and changes nothing by itself. The runner passes a tag expression (Gradle includeTags/excludeTags, Maven groups/excludedGroups) and the JUnit Platform filters non-matching tests out of the run.

open as a page

In JUnit 5 parallel execution, what is the difference between the configuration parameters junit.jupiter.execution.parallel.mode.default and junit.jupiter.execution.parallel.mode.classes.default, and how does the @Execution annotation interact with them?

level: middleimportance: must knowfreq 38%
basics
~20 s

mode.classes.default decides whether top-level test classes may run concurrently with each other; mode.default is the default mode for the rest of the tree — methods and nested classes. Both take same_thread or concurrent. @Execution(CONCURRENT|SAME_THREAD) on a class or method overrides the configured default for that node and everything nested inside it.

open as a page

In JUnit 5, what does the @ResourceLock annotation do, and when would you reach for it?

level: middleimportance: must knowfreq 34%
basics
~20 s

@ResourceLock declares that a test class or method needs a named shared resource. When tests run in parallel, JUnit will not let two tests hold conflicting locks on the same key at once, so tests that touch the same shared state are serialised while everything else keeps running concurrently.

open as a page

JUnit 5 is usually described as three sub-projects rather than one library. What are they, and what is each one responsible for when tests actually run?

level: juniorimportance: must knowfreq 58%
basics
~20 s

JUnit 5 = Platform + Jupiter + Vintage. The Platform is the foundation: it launches tests and defines the TestEngine plug-in interface. Jupiter is the new programming model (annotations, assertions) plus its own engine. Vintage is an engine that runs old JUnit 3 and 4 tests.

open as a page

You need to discover and run JUnit tests from your own Java code — for example inside a custom tool that picks which tests to run — rather than letting a build tool do it. Which JUnit Platform API do you use, and what are the steps?

level: middleimportance: must knowfreq 30%
basics
~20 s

Use the JUnit Platform Launcher API from junit-platform-launcher: build a LauncherDiscoveryRequest with selectors and filters, open a LauncherSession to get a Launcher, register a TestExecutionListener, then call discover() to inspect the TestPlan or execute() to run it.

open as a page

In JUnit 5, how do you declare a test suite that runs a chosen set of test classes, and which annotations decide what goes into it?

level: juniorimportance: should knowfreq 38%
basics
~10 s

Put @Suite on a plain class, then add selectors: @SelectClasses lists test classes explicitly, @SelectPackages scans whole packages. The junit-platform-suite-engine artifact must be on the test runtime classpath, otherwise the suite class runs nothing.

open as a page

In a JUnit 5 project, junit-jupiter-api is normally a compile-scope test dependency while junit-jupiter-engine is runtime-only. Why is the API split from the engine at all, and what does the aggregate junit-jupiter artifact contain?

level: middleimportance: should knowfreq 42%
basics
~20 s

Test code compiles only against the API (annotations, assertions, extensions); the engine is an implementation found at runtime through ServiceLoader. Keeping the engine off the compile path stops code depending on engine internals. The aggregate junit-jupiter artifact bundles api plus params, with the engine at runtime.

open as a page

JUnit's platform reads settings such as parallel test execution and the default test-instance lifecycle from configuration parameters. Where can those parameters be supplied, and which source wins when two of them disagree?

level: middleimportance: should knowfreq 42%
basics
~20 s

Configuration parameters are simple key/value settings. They can come from the LauncherDiscoveryRequest, a JVM system property, or a junit-platform.properties file at the root of the classpath — and that is also the precedence order, request first, file last.

open as a page