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: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
Chaturmind
← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 8+ Years (Senior & Lead)Testing Strategy & API Design
✓ FreeAdvanced· 8 min read

JUnit 5 & Mockito, Advanced — Interview Questions

Nested tests, @TestInstance(PER_CLASS), @BeforeAll differences between JUnit 4 and 5, conditional disabling, @ExtendWith and the extension model, timeouts, TestNG vs JUnit (and running both), the dangers of overusing mocks and @InjectMocks, verifying invocation order, mocking private methods (and why not to), and PowerMock vs Mockito.

Published September 25, 2026


How to use this lesson

The basics (@Mock, when/thenReturn, ArgumentCaptor, parameterised tests) are covered in the 2–5 years tier. Here, the focus is test design judgement: over-mocking, brittle verification, and structuring large suites with JUnit 5's features.

Q1. How do you structure nested tests in JUnit 5?

Short answer: Use @Nested inner (non-static) classes to group tests by scenario or state, each with its own @BeforeEach, which inherits the outer setup. Add @DisplayName to produce readable, specification-like reports:

@DisplayName("Cart")
class CartTest {
    Cart cart;
    @BeforeEach void newCart() { cart = new Cart(); }

    @Nested @DisplayName("when empty")
    class WhenEmpty {
        @Test void totalIsZero() { assertThat(cart.total()).isEqualByComparingTo("0"); }
        @Test void checkoutIsRejected() { assertThatThrownBy(cart::checkout).isInstanceOf(EmptyCartException.class); }
    }

    @Nested @DisplayName("with one item")
    class WithOneItem {
        @BeforeEach void addItem() { cart.add(new Item("pen", new BigDecimal("20")), 2); }
        @Test void totalReflectsQuantity() { assertThat(cart.total()).isEqualByComparingTo("40"); }
        @Test void removingItemEmptiesCart() { cart.remove("pen"); assertThat(cart.isEmpty()).isTrue(); }
    }
}

Q2. What's @TestInstance(Lifecycle.PER_CLASS) for? How does @BeforeAll differ between JUnit 4 and 5?

Short answer:

  • By default (PER_METHOD), JUnit 5 creates a new test-class instance for every test method, which gives strong isolation. So @BeforeAll/@AfterAll must be static, like JUnit 4's @BeforeClass/@AfterClass.
  • @TestInstance(Lifecycle.PER_CLASS): one instance for all the tests in the class, so @BeforeAll/@AfterAll can be non-static, and can use instance fields. That's useful for @Nested classes (which can't have static methods before Java 16), for expensive shared setup, and for Kotlin. The risk: state leaks between tests, so reset mutable fields in @BeforeEach.
  • JUnit 4: @BeforeClass was always static. The names changed too: @Before/@After became @BeforeEach/@AfterEach.

Q3. How do you disable a test conditionally in JUnit 5?

Short answer: Use the built-in conditions:

  • @Disabled("reason") (unconditionally);
  • @EnabledOnOs(LINUX)/@DisabledOnOs(WINDOWS);
  • @EnabledOnJre(JAVA_21)/@EnabledForJreRange(min = JAVA_17);
  • @EnabledIfEnvironmentVariable(named = "CI", matches = "true");
  • @EnabledIfSystemProperty(...);
  • @EnabledIf("customCondition") (a method returning a boolean);
  • @EnabledInNativeImage.

For runtime checks, use assumptions: assumeTrue(DockerClientFactory.instance().isDockerAvailable()) aborts, rather than fails, the test. Testcontainers offers @Testcontainers(disabledWithoutDocker = true). Spring adds @EnabledIf(expression = "${feature.enabled}", loadContext = true). Use tags (@Tag("slow")) to include or exclude groups at build time.

Q4. What's the role of @ExtendWith in JUnit 5?

Short answer: It registers extensions, JUnit 5's single, composable extension model, which replaced JUnit 4's @RunWith runners (only one allowed) and @Rules. Extensions hook into the lifecycle through interfaces:

  • BeforeAllCallback/BeforeEachCallback, and the after-callbacks;
  • ParameterResolver (inject parameters into test methods and constructors);
  • TestExecutionExceptionHandler;
  • ExecutionCondition;
  • TestInstancePostProcessor;
  • InvocationInterceptor.

Examples:

  • SpringExtension (included by @SpringBootTest);
  • MockitoExtension (@Mock fields, and strict stubs);
  • Testcontainers' extension;
  • a custom @ExtendWith(TimingExtension.class).

Several extensions can be combined, and they can be registered through meta-annotations, or programmatically with @RegisterExtension fields (for configured instances).

Q5. How do you test timeouts in JUnit 5?

Short answer:

  • @Timeout(value = 2, unit = SECONDS) on a test, class or lifecycle method: the test fails if it exceeds the limit. It can be set globally (junit.jupiter.execution.timeout.default).
  • assertTimeout(Duration.ofMillis(500), () -> service.compute()): it runs in the same thread, and fails after completion if too slow.
  • assertTimeoutPreemptively(...): runs the code in another thread, and aborts at the timeout. Caution: ThreadLocal-based context (Spring transactions, security) isn't available on that thread.
  • For asynchronous code, prefer Awaitility with atMost(...).

Use timeouts to catch hangs (deadlocks, a missing mock that makes a call block), not as precise performance tests (use JMH for those).

Q6. When would you use TestNG over JUnit? Can both be used in one project?

Short answer:

  • TestNG's historical strengths:

    • test dependencies (dependsOnMethods);
    • data providers;
    • flexible groups and suites (XML configuration);
    • built-in parallel execution options;
    • @BeforeSuite/@BeforeTest hooks.

    It's popular for Selenium or UI and end-to-end suites, and in some QA frameworks.

  • JUnit 5 now covers most of this: parameterised tests, tags, parallel execution, nested tests, and extensions. It's the default in the Spring ecosystem.

  • Choose TestNG when an existing QA framework or suite depends on it, or you need test-dependency ordering for workflow-style tests (often a smell in unit tests).

  • Both in one project: yes. The JUnit Platform can run TestNG tests through the TestNG engine for the JUnit Platform (and JUnit 4 through Vintage), so Maven Surefire or Gradle run everything together. Keep the conventions consistent per module, to avoid confusion.

Q7. What are the problems with overusing mocks?

Short answer:

  • Tests verify implementation, not behaviour: heavy verify calls and stubs mirror the code's internals, so every refactor breaks the tests even when the behaviour is unchanged (brittle tests).
  • False confidence: mocks encode your assumptions about collaborators. When the real dependency behaves differently (SQL semantics, serialisation, HTTP errors), the tests still pass, and production fails.
  • Mocking what you don't own (JPA EntityManager, RestTemplate, AWS SDKs) is especially fragile. Use integration tests (Testcontainers, WireMock) or fakes instead.
  • Setup complexity: long stubbing chains (deep stubs) are hard to read.
  • Design smell: needing many mocks suggests a class with too many responsibilities or dependencies.

Better balance:

  • mock at architectural boundaries (ports and adapters);
  • use real objects for value types and simple collaborators;
  • use in-memory fakes for repositories in pure unit tests;
  • test the behaviour and outcomes (state), rather than interactions, unless the interaction is the behaviour (for example, "sends an email").

Q8. What are the risks of overusing @InjectMocks?

Short answer: @InjectMocks tries constructor, then setter, then field injection of the @Mocks, and silently leaves dependencies null if it can't match them (a type mismatch, or several constructors). That gives NPEs far from the cause, or tests that pass while half the collaborators are unset. It also:

  • hides the class's real dependencies from the test reader;
  • can inject by field reflection, which bypasses constructor invariants;
  • doesn't help you notice a class with too many dependencies.

Prefer explicit construction: var service = new OrderService(repo, payments, clock);. It's type-checked, readable, and fails to compile when the dependencies change.

Q9. How do you verify the order of method invocations?

Short answer: Use Mockito's InOrder:

InOrder inOrder = inOrder(inventory, payments, notifications);
inOrder.verify(inventory).reserve(order);
inOrder.verify(payments).charge(order);
inOrder.verify(notifications).sendConfirmation(order);
inOrder.verifyNoMoreInteractions();

Use it only when the order matters to the behaviour (reserve before charge, and compensation order in sagas). Otherwise, it over-specifies the implementation, and makes the tests brittle.

Q10. Can you mock private methods? How?

Short answer: Mockito can't: private methods aren't overridable, and it shouldn't be done. Private methods are implementation details, so test them through the public behaviour. If a private method is complex enough to want its own tests, extract it into a separate class (or a package-private method), and test that directly. PowerMock could stub private methods through bytecode manipulation, but it's effectively abandoned (it doesn't support Java 17+ or JUnit 5 properly). Reflection (ReflectionTestUtils.invokeMethod) can call private methods, but it also couples the tests to the internals.

Q11. How is PowerMock different from Mockito?

Short answer:

  • Mockito creates mocks through subclassing or ByteBuddy inline instrumentation. Since Mockito 2 (with the inline mock maker, the default in Mockito 5), it can also mock final classes and methods, and since Mockito 3.4, static methods (mockStatic in try-with-resources) and constructors (mockConstruction). It's actively maintained, and works with JUnit 5 and modern Java.
  • PowerMock used a custom class loader and bytecode manipulation to mock statics, constructors, finals and privates, when Mockito couldn't. It's incompatible with modern Java (strong encapsulation) and JUnit 5, it's slow, and it's unmaintained.

Migration: replace PowerMock with Mockito's inline features. Better still, refactor away the static calls (inject a Clock, wrap static utilities behind interfaces).

try (MockedStatic<UUID> uuid = mockStatic(UUID.class)) {         // Mockito inline: scoped static mocking
    uuid.when(UUID::randomUUID).thenReturn(FIXED);
    assertThat(service.newOrderId()).isEqualTo(FIXED.toString());
}

Follow-up questions this topic invites — and their answers

Q: What are Mockito's strict stubs? A: With MockitoExtension (strictness STRICT_STUBS), unused stubbings fail the test, and argument mismatches are reported clearly. That keeps tests lean, and catches wrong stubs.

Q: How do you test code that uses LocalDateTime.now()? A: Inject a java.time.Clock (with Clock.fixed(...) in tests), and use LocalDateTime.now(clock). That avoids static mocking entirely.

Q: What's the difference between a fake, a stub, a mock and a spy? A: A stub returns canned answers. A mock is a stub that also verifies interactions. A fake is a working lightweight implementation (an in-memory repository). A spy wraps a real object, and records or partially stubs its calls.

Q: What's a good unit test naming convention? A: Something describing the behaviour: methodUnderTest_condition_expectedOutcome, or a @DisplayName sentence like "rejects checkout when the cart is empty". Aim for tests that read like a specification.

Previous

Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions

AI Tutor

Lesson: JUnit 5 & Mockito, Advanced — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.