Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 2–5 Years

Revise the Fresher Tier

  • Revise: Core Java Fundamentals (Fresher Tier)
  • Revise: Collections, Threads, Java 8 & Coding (Fresher Tier)
  • Revise: Spring Core, Spring Boot & Spring MVC (Fresher Tier)
  • Revise: Hibernate, SQL, Microservices, Maven & Git (Fresher Tier)

Intermediate Core Java

  • Intermediate OOP & Language Features — Interview Questions
  • Enums, Generics, Pass-by-Value & Imports — Interview Questions
  • Collections Internals & Scenarios — Interview Questions
  • JVM, Memory & Class Loading — Interview Questions
  • Exception Design & Serialization Edge Cases — Interview Questions

Java 8 Deep Dive & Stream Coding

  • Lambdas, Functional Interfaces & Method References — Interview Questions
  • Default Methods, Backward Compatibility & Optional — Interview Questions
  • Stream API Internals (Part 1) — Interview Questions
  • Stream API Internals (Part 2) — Interview Questions
  • Stream Coding Problems (Level 2, Part 1) — Interview Questions
  • Stream Coding Problems (Level 2, Part 2) — Interview Questions

Concurrency & Multithreading

  • Synchronization, Locks & Deadlocks — Interview Questions
  • Executors, ThreadLocal & Concurrent Collections — Interview Questions

Spring Framework In Depth

  • Bean Lifecycle, Contexts & Circular Dependencies — Interview Questions
  • Spring Bean Conflicts, AOP, Batch & Injection Styles — Interview Questions

Spring Boot In Depth & Scenarios

  • Spring Boot Internals & Auto-Configuration — Interview Questions
  • Spring Boot Configuration, Profiles & Secrets — Interview Questions
  • Spring Boot Data, Transactions & Caching — Interview Questions
  • Spring Boot Testing, Error Pages & Deployment — Interview Questions
  • Spring Boot Performance, Scaling & Resilience — Interview Questions
  • Spring Boot Async, Events & Messaging — Interview Questions
  • Spring Boot Security Scenarios — Interview Questions
  • Actuator, AOP, Spring Cloud & Distributed Tracing — Interview Questions
  • External APIs, Files, GraphQL & WebFlux — Interview Questions

Spring Security

  • Spring Security — OAuth2, CORS, CSRF & Access Rules — Interview Questions
  • Spring Security — Passwords, Filter Chain, Sessions & Debugging — Interview Questions

Apache Kafka

  • Kafka Architecture, Topics & ZooKeeper vs KRaft — Interview Questions
  • Kafka Producers, Consumers, Reliability & Streams — Interview Questions
  • Kafka Security, Connect & Core Scenarios — Interview Questions
  • Kafka Production Scenarios — Interview Questions

Git, Maven & Gradle (Level II)

  • Git Workflows, Rebase & Conflict Handling — Interview Questions
  • Git Recovery, Hooks, Tags & Collaboration — Interview Questions
  • Maven Builds, Multi-Module Projects & Dependency Resolution — Interview Questions
  • Maven Profiles, settings.xml, Plugins & Quality Gates — Interview Questions
  • Gradle Fundamentals, Migration & Dependencies — Interview Questions
  • Gradle Performance, Plugins, Testing & Publishing — Interview Questions

Deployment & CI/CD

  • Sessions, CI/CD Pipelines, Rollbacks & Secrets — Interview Questions
  • Migrations, Zero-Downtime Releases, Containers & Monitoring — Interview Questions

JUnit 5 & Mockito

  • JUnit 5 — Interview Questions
  • Mockito Basics — Interview Questions
  • Mockito Advanced & Tricky Questions — Interview Questions
Chaturmind
← Java Interview Prep: 2–5 Years

Revise the Fresher Tier

  • Revise: Core Java Fundamentals (Fresher Tier)
  • Revise: Collections, Threads, Java 8 & Coding (Fresher Tier)
  • Revise: Spring Core, Spring Boot & Spring MVC (Fresher Tier)
  • Revise: Hibernate, SQL, Microservices, Maven & Git (Fresher Tier)

Intermediate Core Java

  • Intermediate OOP & Language Features — Interview Questions
  • Enums, Generics, Pass-by-Value & Imports — Interview Questions
  • Collections Internals & Scenarios — Interview Questions
  • JVM, Memory & Class Loading — Interview Questions
  • Exception Design & Serialization Edge Cases — Interview Questions

Java 8 Deep Dive & Stream Coding

  • Lambdas, Functional Interfaces & Method References — Interview Questions
  • Default Methods, Backward Compatibility & Optional — Interview Questions
  • Stream API Internals (Part 1) — Interview Questions
  • Stream API Internals (Part 2) — Interview Questions
  • Stream Coding Problems (Level 2, Part 1) — Interview Questions
  • Stream Coding Problems (Level 2, Part 2) — Interview Questions

Concurrency & Multithreading

  • Synchronization, Locks & Deadlocks — Interview Questions
  • Executors, ThreadLocal & Concurrent Collections — Interview Questions

Spring Framework In Depth

  • Bean Lifecycle, Contexts & Circular Dependencies — Interview Questions
  • Spring Bean Conflicts, AOP, Batch & Injection Styles — Interview Questions

Spring Boot In Depth & Scenarios

  • Spring Boot Internals & Auto-Configuration — Interview Questions
  • Spring Boot Configuration, Profiles & Secrets — Interview Questions
  • Spring Boot Data, Transactions & Caching — Interview Questions
  • Spring Boot Testing, Error Pages & Deployment — Interview Questions
  • Spring Boot Performance, Scaling & Resilience — Interview Questions
  • Spring Boot Async, Events & Messaging — Interview Questions
  • Spring Boot Security Scenarios — Interview Questions
  • Actuator, AOP, Spring Cloud & Distributed Tracing — Interview Questions
  • External APIs, Files, GraphQL & WebFlux — Interview Questions

Spring Security

  • Spring Security — OAuth2, CORS, CSRF & Access Rules — Interview Questions
  • Spring Security — Passwords, Filter Chain, Sessions & Debugging — Interview Questions

Apache Kafka

  • Kafka Architecture, Topics & ZooKeeper vs KRaft — Interview Questions
  • Kafka Producers, Consumers, Reliability & Streams — Interview Questions
  • Kafka Security, Connect & Core Scenarios — Interview Questions
  • Kafka Production Scenarios — Interview Questions

Git, Maven & Gradle (Level II)

  • Git Workflows, Rebase & Conflict Handling — Interview Questions
  • Git Recovery, Hooks, Tags & Collaboration — Interview Questions
  • Maven Builds, Multi-Module Projects & Dependency Resolution — Interview Questions
  • Maven Profiles, settings.xml, Plugins & Quality Gates — Interview Questions
  • Gradle Fundamentals, Migration & Dependencies — Interview Questions
  • Gradle Performance, Plugins, Testing & Publishing — Interview Questions

Deployment & CI/CD

  • Sessions, CI/CD Pipelines, Rollbacks & Secrets — Interview Questions
  • Migrations, Zero-Downtime Releases, Containers & Monitoring — Interview Questions

JUnit 5 & Mockito

  • JUnit 5 — Interview Questions
  • Mockito Basics — Interview Questions
  • Mockito Advanced & Tricky Questions — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 2–5 YearsJUnit 5 & Mockito
✓ FreeIntermediate· 9 min read

JUnit 5 — Interview Questions

Why JUnit matters, lifecycle annotations (JUnit 4 vs 5), testing exceptions, assertion types, parameterized tests, suites, timeouts, test structure, @Test, static methods without libraries, runners/rules vs extensions, private methods, database-free tests, parallel execution, and best practices.

Published September 25, 2026


How to use this lesson

Many interview questions are still phrased in JUnit 4 terms (@Before, @RunWith, @Rule). Answer with the JUnit 5 (Jupiter) equivalent, and mention the JUnit 4 name. That shows you know both, and that you use the current one.

Q1. What is JUnit, and why is it important?

Short answer: JUnit is the standard testing framework for Java. It discovers test methods, runs them with a lifecycle, provides assertions, and reports the results to IDEs and build tools. JUnit 5 has three parts: the Platform (the launcher), Jupiter (the programming model) and Vintage (runs JUnit 4 tests). Tests give fast feedback, catch regressions before production, document behaviour, and make refactoring safe.

Learn it in depth → Spring Boot Testing & Auto-Configuration

Q2. What's the difference between @Before and @BeforeClass? How are they used?

Short answer: They're JUnit 4 annotations:

  • @Before runs before each test method: fresh fixtures per test.
  • @BeforeClass runs once before all tests in the class. It must be static, and is used for expensive shared setup.

JUnit 5 renames them @BeforeEach and @BeforeAll (with @AfterEach and @AfterAll). @BeforeAll can be non-static with @TestInstance(Lifecycle.PER_CLASS).

class OrderServiceTest {
    static PostgreSQLContainer<?> db;
    OrderService service;

    @BeforeAll static void startDb() { db = new PostgreSQLContainer<>("postgres:16"); db.start(); }   // once
    @BeforeEach void setUp() { service = new OrderService(new InMemoryOrders(), Clock.systemUTC()); }   // per test
    @AfterAll static void stopDb() { db.stop(); }
}

Key points to cover:

  • Keep per-test state in @BeforeEach, so tests stay independent. Shared mutable state in @BeforeAll causes order-dependent failures.

Q3. How do you test for expected exceptions?

Short answer: In JUnit 5, use assertThrows. It returns the exception, so you can also assert on its message or fields. (JUnit 4 used @Test(expected = …), or the ExpectedException rule.)

@Test
void rejectsNegativeAmount() {
    var ex = assertThrows(IllegalArgumentException.class, () -> account.withdraw(new BigDecimal("-5")));
    assertEquals("amount must be positive", ex.getMessage());
}

Key points to cover:

  • @Test(expected = …) passes if any line throws that exception, even the setup, which can hide bugs. assertThrows targets the exact call.
  • AssertJ offers a fluent alternative: assertThatThrownBy(() -> …).isInstanceOf(…).hasMessageContaining(…).

Q4. What's the difference between assertEquals, assertTrue and assertSame?

Short answer:

  • assertEquals(expected, actual) compares values with equals(), with a helpful failure message showing both values.
  • assertTrue(condition) checks a boolean, but its failure message only says "expected true".
  • assertSame(expected, actual) checks reference identity (==).

Key points to cover:

  • Prefer specific assertions for clearer failures (assertEquals(3, list.size()) rather than assertTrue(list.size() == 3)).
  • Pass a message supplier for context.
  • Use assertAll to report several failures at once.
  • Floating point: assertEquals(expected, actual, delta). For BigDecimal, compare with compareTo, or AssertJ's isEqualByComparingTo.

Q5. What are parameterized tests, and how do they work?

Short answer: One test method runs once per set of arguments. JUnit 5 uses @ParameterizedTest with sources:

  • @ValueSource, @CsvSource and @CsvFileSource;
  • @MethodSource;
  • @EnumSource;
  • @ArgumentsSource, for custom sources.

(JUnit 4 needed a whole class with @RunWith(Parameterized.class).)

@ParameterizedTest(name = "{0} → {1}")
@CsvSource({ "0, 0", "999, 0", "1000, 50", "5000, 400" })
void discountTiers(int orderTotal, int expectedDiscount) {
    assertEquals(expectedDiscount, pricing.discountFor(orderTotal));
}

@ParameterizedTest
@MethodSource("invalidEmails")
void rejectsInvalid(String email) { assertFalse(validator.isValid(email)); }
static Stream<String> invalidEmails() { return Stream.of("", "a@", "@b.com", "no-at-sign"); }

Q6. What is a test suite, and how do you create one?

Short answer: A suite groups tests to run together, for example "all fast tests" or "the payment module". In JUnit 5, use the Platform Suite engine: @Suite with @SelectPackages, @SelectClasses, @IncludeTags or @ExcludeTags. (JUnit 4: @RunWith(Suite.class) + @Suite.SuiteClasses.)

@Suite
@SelectPackages("com.acme.payments")
@IncludeTags("fast")
class PaymentsFastSuite { }

Key points to cover:

  • In practice, tags plus build-tool filtering (./gradlew test -PincludeTags=fast, Maven <groups>) are more common than suite classes.

Q7. How do you handle timeouts?

Short answer:

  • JUnit 5's @Timeout(value = 2, unit = SECONDS) fails a test (or all tests in a class) that runs too long.
  • assertTimeout runs the code in the same thread and fails after it finishes. assertTimeoutPreemptively aborts the code when time runs out, but beware: that code runs in a different thread, which breaks ThreadLocals and transactions.
  • A default can be set with junit.jupiter.execution.timeout.default.

(JUnit 4: @Test(timeout = 1000).)

Key points to cover:

  • For asynchronous code, don't sleep. Poll with a deadline, using Awaitility: await().atMost(5, SECONDS).until(() -> repo.count() == 1).

Q8. How do you structure a test case?

Short answer: Arrange–Act–Assert (or Given–When–Then), with one behaviour per test, and a descriptive name that states the scenario and expected outcome.

@Test
@DisplayName("placing an order with insufficient stock is rejected and nothing is saved")
void rejectsOrderWhenStockInsufficient() {
    // Arrange
    inventory.setStock("SKU-1", 1);
    // Act
    var result = assertThrows(OutOfStockException.class, () -> orders.place(cartWith("SKU-1", 2)));
    // Assert
    assertEquals("SKU-1", result.sku());
    assertEquals(0, orderRepository.count());
}

Key points to cover:

  • Use @Nested classes to group scenarios ("when the cart is empty…"). Use test data builders or Object Mothers for readable arrangement.

Q9. What is the purpose of @Test?

Short answer: It marks a method as a test that the engine should discover and run. JUnit 5 test methods can be package-private, must not be private or static, and return void. Related annotations: @ParameterizedTest, @RepeatedTest, @TestFactory (dynamic tests), @Disabled, @Tag, @DisplayName.

Common trap: mixing org.junit.Test (JUnit 4) with JUnit 5 imports. The test silently doesn't run unless the Vintage engine is present.

Q10. How do you mock a static method? Is it possible without external libraries?

Short answer: JUnit itself can't mock anything. It's a test runner, not a mocking library. Without extra libraries, the answer is design: wrap the static call behind an interface you can substitute (Clock, an IdGenerator, a CurrentTime provider), and inject it. With libraries, Mockito's mockStatic (built in since Mockito 5, with the inline mock maker) works, scoped with try-with-resources.

// Refactor instead of mocking: inject a Clock instead of calling LocalDate.now()
class InvoiceService {
    private final Clock clock;
    InvoiceService(Clock clock) { this.clock = clock; }
    LocalDate dueDate() { return LocalDate.now(clock).plusDays(30); }
}
// test: new InvoiceService(Clock.fixed(Instant.parse("2026-01-01T00:00:00Z"), ZoneOffset.UTC))

Q11. How do @RunWith and @Rule work? What replaced them?

Short answer: In JUnit 4:

  • @RunWith replaces the whole runner. For example, SpringRunner, MockitoJUnitRunner or Parameterized. Only one runner is allowed per class.
  • @Rule / @ClassRule wrap each test (or the class) with reusable behaviour: TemporaryFolder, ExpectedException, Timeout.

JUnit 5 replaced both with the extension model: @ExtendWith(...). It's composable, so several extensions can be combined (SpringExtension, MockitoExtension, Testcontainers), using lifecycle callbacks, parameter resolvers and conditions.

@ExtendWith({ MockitoExtension.class })
class CheckoutServiceTest { @TempDir Path tmp; /* built-in replacement for TemporaryFolder */ }

Q12. How do you test private methods? Should you test them directly?

Short answer: Test them through the public behaviour that uses them. Private methods are implementation details, and tests coupled to them break on every refactor. If a private method is complex enough to want its own tests, that's a design signal: extract it into its own class (or a package-private helper), with a public contract, and test that.

Key points to cover:

  • Reflection-based access (setAccessible(true)) is possible, but brittle, and blocked by modules for non-open packages. Avoid it.

Q13. How do you test a method that calls the database, without hitting the real database?

Short answer: Separate the unit and integration concerns:

  • Unit test the service logic with the repository mocked (Mockito), or with a simple in-memory fake. Fast and focused.
  • Test the data access itself with a real database in a container (Testcontainers + @DataJpaTest). Queries, mappings and constraints can only be proven against the real engine.

Common trap: relying on H2 to "test the database". Its SQL dialect, locking and constraint behaviour differ from PostgreSQL or MySQL, so the tests pass while production fails.

Q14. How does JUnit handle running tests in parallel?

Short answer: JUnit 5 supports opt-in parallel execution through configuration (junit-platform.properties):

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.mode.classes.default=concurrent
junit.jupiter.execution.parallel.config.strategy=dynamic

Key points to cover:

  • Tests must be independent, with no shared static or mutable state.
  • Use @ResourceLock for shared resources, and @Execution(SAME_THREAD) for tests that can't run concurrently.
  • Build tools can also parallelise across JVM forks (Maven Surefire's forkCount, Gradle's maxParallelForks), which isolates the static state.

Q15. What are the best practices for unit tests with JUnit?

Short answer:

  • Fast, isolated, repeatable and self-validating (FIRST). No sleeps, no real network, no dependency on test order.
  • One behaviour per test, with descriptive names, and Arrange–Act–Assert.
  • Test edge cases and failure paths, not just the happy path.
  • Use fluent assertions (AssertJ). Don't over-mock: fakes for simple collaborators, mocks at the boundaries.
  • Deterministic time and randomness: an injected Clock and seeds.
  • Keep tests as clean as production code, and run them on every PR in CI.
  • Measure coverage as a guide, not a goal. Mutation testing (PIT) shows whether the tests actually catch bugs.

Follow-up questions this topic invites — and their answers

Q: What is @Nested for? A: Grouping related tests in inner classes, each with its own setup, to express scenarios ("given an empty cart", "given a premium customer"), with readable reports.

Q: What does @TestInstance(Lifecycle.PER_CLASS) change? A: JUnit creates one test instance per class instead of per method, so @BeforeAll can be non-static and instance state is shared. Use it carefully.

Q: How do you disable a test conditionally? A: @Disabled("reason"), or conditions such as @EnabledOnOs, @EnabledIfEnvironmentVariable, @EnabledIfSystemProperty, or a custom ExecutionCondition extension.

Q: What are dynamic tests? A: Tests generated at runtime by a @TestFactory method, which returns a Stream<DynamicTest>. They're useful when the test cases come from data files.

Previous

Migrations, Zero-Downtime Releases, Containers & Monitoring — Interview Questions

Next

Mockito Basics — Interview Questions

AI Tutor

Lesson: JUnit 5 — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.