In this article
How to use this page
The wider path is in coding interviews, and the place of a testing round in a loop is in interview loops. Use this page alongside the other Java rounds: Java object-oriented questions, Spring Boot questions and representational state transfer (REST) application programming interface (API) questions.
JUnit versions and architecture
What are the JUnit Platform, JUnit Jupiter and JUnit Vintage?
JUnit has three parts. The Platform is the foundation for launching testing frameworks on the Java Virtual Machine (JVM) and defines the engine interface. Jupiter is the programming model and extension model for writing tests. Vintage is an engine that runs JUnit 3 and 4 tests, which the guide calls deprecated and for temporary use while migrating. Version 6.1.3 needs Java 17 or higher at runtime (JUnit User Guide, October 2026).
Why it is asked: it shows whether you know JUnit is a platform with engines, not one library. Read the full explanation: JUnit User Guide, overview.
What changed between JUnit 4 and JUnit Jupiter?
The lifecycle annotations were renamed: @Before and @After became @BeforeEach and @AfterEach, and @BeforeClass and @AfterClass became @BeforeAll and @AfterAll. @Ignore became @Disabled and @Category became @Tag. Assertions live in org.junit.jupiter.api.Assertions. The expected attribute of @Test and the ExpectedException rule no longer exist, and assertThrows replaces them.
Why it is asked: many codebases still mix both generations. Read the full explanation: JUnit User Guide, migrating from JUnit 4.
What replaced @RunWith, @Rule and @ClassRule?
An extension is a plug-in that adds behaviour to tests, such as dependency injection or conditional execution. The extension model replaces the three JUnit 4 mechanisms: @ExtendWith registers extensions declaratively and @RegisterExtension registers them from code. @RunWith(Enclosed.class) becomes @Nested, and @RunWith(Parameterized.class) maps to parameterized test classes (@ParameterizedClass), as the migration guide words it.
Why it is asked: it tests whether you know how Jupiter is extended. Read the full explanation: JUnit User Guide, migrating from JUnit 4.
Knowing it is half the job. Saying it is the other half.
Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.
Start Free Trial →Lifecycle and structure
In what order do the lifecycle methods run?
Setup and cleanup run in a fixed order. For a class with two tests, the recorded order was @BeforeAll, then @BeforeEach, the test and @AfterEach for the first test, then the same three for the second, then @AfterAll. The @BeforeAll and @AfterAll methods ran once, and @BeforeEach and @AfterEach ran twice.1
Text description of the diagram
- The run order for a class with two tests was BeforeAll, BeforeEach, Test, AfterEach, BeforeEach, Test, AfterEach, AfterAll.
- BeforeAll and AfterAll ran once, and BeforeEach and AfterEach ran twice.
Why it is asked: shared setup in the wrong hook causes slow or order-dependent tests. Read the full explanation: JUnit User Guide, annotations.
Why must a @BeforeAll method be static?
By default JUnit creates a new instance of the test class for every test method, so a method that runs before all tests cannot belong to an instance. In the run, a non-static @BeforeAll failed discovery with the message that it must be static unless the class is annotated for per-class lifecycle. Annotating the class with @TestInstance(Lifecycle.PER_CLASS) allows non-static methods.1
Why it is asked: it probes your model of the instance lifecycle. Read the full explanation: JUnit User Guide, test instance lifecycle.
Do tests share a test instance?
A test instance is the object of the test class on which a test method runs. By default each test method gets its own, created before the method runs. In the run, a class with two tests used two instances; with the per-class lifecycle it used one, so state kept in fields then carries from one test to the next unless you reset it in @BeforeEach.1
Why it is asked: shared state between tests is a common cause of order-dependent failures. Read the full explanation: JUnit User Guide, test instance lifecycle.
How do you skip a test?
Annotate the class or method with @Disabled, optionally with a reason; the guide calls it analogous to JUnit 4's @Ignore. In the run, a class with one disabled and one normal test reported one skipped and one succeeded, and the disabled body never executed.1 Conditions such as the operating system or an environment variable are handled by the built-in execution conditions.
Why it is asked: a skipped test with no reason hides debt. Read the full explanation: JUnit User Guide, annotations.
How do you group and filter tests?
Three annotations help. @Nested marks a non-static nested class, so tests can be grouped by scenario inside one file. @Tag declares tags at class or method level for filtering, for example to run only fast tests in a build. @DisplayName gives a class or method a readable name in reports.
Why it is asked: it separates people who write one flat test class from those who organise a suite. Read the full explanation: JUnit User Guide, annotations.
Assertions
What is the argument order of assertEquals, and where does the message go?
The examples in the guide put the expected value first and the actual value second, as in assertEquals(2, calculator.add(1, 1)). In Jupiter the optional failure message is the last parameter, where JUnit 4 had it first. A failing assertEquals(2, 5) produced the message "expected: <2> but was: <5>", so swapping the arguments produces a misleading message.1
Why it is asked: swapped arguments are a common review comment. Read the full explanation: JUnit User Guide, assertions.
How do you test that code throws an exception?
Use assertThrows with the exception type and a lambda holding the code. It fails if nothing is thrown or if the exception is not an instance of the expected type; a subclass passes, and assertThrowsExactly demands the exact type. It returns the exception so you can assert on it. In the run, dividing an integer by zero returned an ArithmeticException whose message was "/ by zero". The old @Test(expected = ...) form no longer exists.1
Why it is asked: exception testing shows whether you migrated from JUnit 4. Read the full explanation: JUnit User Guide, assertions.
What does assertAll do?
It groups assertions so that all of them run and all failures are reported together. The guide says that in a grouped assertion all assertions are executed and all failures reported together. In the run, a group of three failing assertions raised one error holding three failures, where three separate assertions would have stopped at the first.1
Why it is asked: it tests whether you design tests for useful failure messages. Read the full explanation: JUnit User Guide, assertions.
What is the difference between assertTimeout and assertTimeoutPreemptively?
Both fail a test that takes too long. assertTimeout runs the code on the calling thread and lets it finish, while assertTimeoutPreemptively runs it on a different thread and abandons it at the limit. In the run, the first stayed on the test's thread and the second did not.1 The guide warns that a different thread can break code that relies on thread-local storage, such as Spring transaction handling.
Why it is asked: it probes thread awareness in test code. Read the full explanation: JUnit User Guide, assertions.
Should you use JUnit assertions or a library such as AssertJ?
Both work. The guide says the built-in assertions are sufficient for many tests and, when more power is needed, the JUnit team recommends third-party libraries such as AssertJ, Hamcrest and Truth. The built-in assertions need no extra dependency and are enough for simple checks. Pick one style per project and enforce it, for example with a build rule.
Why it is asked: it checks whether you have an opinion about readability. Read the full explanation: JUnit User Guide, assertions.
Parameterized and dynamic tests
How do parameterized tests work?
A @ParameterizedTest runs one test method several times with different arguments, and each run is reported separately. You must add at least one source: @ValueSource for literal values, @CsvSource for comma-separated records, @MethodSource for a factory method, @EnumSource for enum constants. In the run, a three-row @CsvSource gave three invocations named by a custom pattern, case {index}: {0} + {1} = {2} (the default name differs).1
Why it is asked: it tests whether you avoid copy-pasted test methods. Read the full explanation: JUnit User Guide, parameterized tests.
How do @RepeatedTest, @ParameterizedTest and @TestFactory differ?
@ParameterizedTest runs a method with different arguments. @RepeatedTest is a test template that runs the same test a set number of times, useful for flaky or randomised code. @TestFactory marks a method that builds dynamic tests at run time, so the number and content of tests can depend on data.
Why it is asked: it separates data-driven tests from tests generated by code. Read the full explanation: JUnit User Guide, annotations.
Test doubles and Mockito
What are the kinds of test double?
A test double is any object that stands in for a real collaborator during a test. Martin Fowler, using Meszaros's vocabulary, lists five: a dummy is passed around but never used; a fake has a working shortcut implementation, such as an in-memory database; a stub gives canned answers; a spy is a stub that also records how it was called; a mock is pre-programmed with expectations of the calls it should receive (Fowler, January 2007).
Why it is asked: the words are used loosely, and precision shows experience. Read the full explanation: Martin Fowler, test doubles article.
What is the difference between state verification and behaviour verification?
State verification checks the state of the system under test and its collaborators after the call, for example that an order is filled and stock reduced. Behaviour verification checks that the system made the right calls on its collaborators, with expectations set up first, the style Fowler describes; Mockito checks the calls afterwards with verify. According to Fowler, a mock object always uses behaviour verification, while a stub can support either.
Why it is asked: it explains why some tests break on refactoring. Read the full explanation: Martin Fowler, test doubles article.
What does an unstubbed Mockito mock return?
A Mockito mock returns a default for every method you have not stubbed: zeros, empty collections, nulls and the like, "as appropriate" to the return type in the Javadoc's words. So a mock of a service that returns a list gives an empty list, not an exception. Stub only what the test needs, and verify calls with verify, which checks one call by default.
Why it is asked: unexpected nulls from mock objects cause confusing failures. Read the full explanation: Mockito class Javadoc.
How does a Mockito spy differ from a mock, and why use doReturn with it?
A spy is a copy of a real object whose real methods run unless you stub them; Mockito copies the instance rather than delegating to it, so changes made through the original are not seen by the spy. The Javadoc connects this to partial mocking. Because when(spy.method()) would call the real method while stubbing, the documentation advises the doReturn, doAnswer or doThrow family for spies. Use a spy sparingly: needing one often signals a class that does too much.
Why it is asked: it probes the pitfalls of partial mocking. Read the full explanation: Mockito class Javadoc.
Can Mockito mock final classes and methods?
Yes, as of October 2026 (Mockito 5). The Javadoc says that starting from the first Mockito 5 release the inline mock maker became the default mock maker, so mocking final classes and methods is enabled by default. From Java 21 the Java Development Kit (JDK) restricts a library from attaching an agent to its own JVM, so the Javadoc says the inline mock maker might need an explicit -javaagent setup and the JVM prints a warning. In older Mockito versions you needed extra configuration, so check your versions before assuming either way.
Why it is asked: legacy projects still ship old Mockito versions. Read the full explanation: Mockito class Javadoc.
How many times does verify check a call by default?
Once. In the Javadoc, times(1) is the default, and writing it explicitly can be omitted, so verify(service).save(order) fails if the call happened twice or never. Use times(n), never() or atLeast(n) when you mean something else, and prefer asserting on outcomes when behaviour checks would tie the test to the implementation.
Why it is asked: it checks whether you know what a plain verify promises. Read the full explanation: Mockito class Javadoc.
- JUnit has three parts: the Platform that launches test engines, Jupiter for writing tests, and the deprecated Vintage engine for JUnit 3 and 4.
- BeforeAll and AfterAll run once per class; BeforeEach and AfterEach wrap every test, each on a fresh instance by default.
- Use assertThrows to capture an exception and assertAll to see every failure at once.
- A parameterized test turns one method into one reported invocation per row of data.
- Know the test doubles by name, and remember that a spy calls real methods unless stubbed.
Sources
- JUnit User Guide: overview — JUnit team, docs.junit.org (version 6.1.3, date = read date), 24 Oct 2026
- JUnit User Guide: annotations — JUnit team, docs.junit.org (date = read date), 24 Oct 2026
- JUnit User Guide: test instance lifecycle — JUnit team, docs.junit.org (date = read date), 24 Oct 2026
- JUnit User Guide: assertions — JUnit team, docs.junit.org (date = read date), 24 Oct 2026
- JUnit User Guide: parameterized classes and tests — JUnit team, docs.junit.org (date = read date), 24 Oct 2026
- JUnit User Guide: migrating from JUnit 4 — JUnit team, docs.junit.org (date = read date), 24 Oct 2026
- Martin Fowler, test doubles article — Martin Fowler, 2 Jan 2007
- Mockito class Javadoc (rendered, javadoc.io) — Mockito project (date = read date), 24 Oct 2026