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· 11 min read

Spring Boot Test Slices, Context & Test Strategy — Interview Questions

@SpringBootTest vs @DataJpaTest vs @WebMvcTest, how slices auto-configure, what happens when @SpringBootTest loads the context (and context caching), testing one layer in isolation, @TestConfiguration, @DirtiesContext, test transactions and preventing rollback, unit vs integration tests, webEnvironment modes, testing service layers, conditional/profile beans, controlling properties, resetting database state, parallel integration tests, embedded H2, and injecting security credentials.

Published September 25, 2026


How to use this lesson

A senior test strategy balances confidence, speed and maintainability:

  • lots of fast unit tests;
  • focused slice tests;
  • a smaller set of integration tests against real infrastructure (Testcontainers);
  • a few end-to-end tests.

Know how Spring's context caching works, because most slow test suites come from breaking it.

Q1. What's the difference between @SpringBootTest, @DataJpaTest and @WebMvcTest? When would you use @WebMvcTest instead of @SpringBootTest?

Short answer:

  • @SpringBootTest: loads the full application context (every bean and auto-configuration). It's optionally with a real server (webEnvironment). It's for integration tests of whole flows. It's the slowest.
  • @WebMvcTest(Controller.class): only the MVC slice: the controllers, @ControllerAdvice, filters, converters, WebMvcConfigurers and Spring Security's web configuration, with MockMvc auto-configured. No services or repositories: mock them with @MockitoBean. It's fast, and focused on request mapping, validation, serialisation, status codes and security rules.
  • @DataJpaTest: only the JPA slice: entities, repositories, EntityManager, DataSource, Flyway or Liquibase, and transactional tests that roll back by default. It replaces the DataSource with an embedded database, unless @AutoConfigureTestDatabase(replace = NONE) (with Testcontainers). It's for repository queries and mappings.

Use @WebMvcTest when you're testing the web contract and don't need the business logic running. It starts much faster, and failures are localised. Use @SpringBootTest for cross-layer behaviour (a transaction plus events plus the web layer).

Learn it in depth → Global Exception Handling

Q2. How does Spring Boot auto-configure test slices like @DataJpaTest and @DataMongoTest?

Short answer: Each slice annotation is a meta-annotation combining:

  • @BootstrapWith (a slice-specific context bootstrapper);
  • @TypeExcludeFilters, which limits component scanning to the relevant stereotypes (for example, only @Repository, or only @Controller/@ControllerAdvice);
  • @ImportAutoConfiguration, which loads only the auto-configurations listed for that slice in META-INF/spring/org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa.imports (and similar files);
  • extra helpers (@Transactional, @AutoConfigureTestDatabase, TestEntityManager, MockMvc).

So a @DataMongoTest gets the Mongo template and repositories (and, with Testcontainers, @ServiceConnection), but no web layer. You can add beans back with @Import or @TestConfiguration, and write your own slices the same way.

Q3. What happens behind the scenes when @SpringBootTest loads the context? How does context caching work?

Short answer:

  1. The SpringBootTestContextBootstrapper finds the @SpringBootConfiguration (by searching the test's package upwards), and builds a MergedContextConfiguration: the configuration classes, active profiles, property sources (properties, @TestPropertySource, @DynamicPropertySource), context customisers (mock beans, @ServiceConnection) and web environment.
  2. It creates the application context through SpringApplication (so the environment, auto-configuration and listeners are real), with the test-specific customisers applied.
  3. The context is cached in a static cache keyed by the MergedContextConfiguration. Other test classes with an identical configuration reuse it, which is huge for speed.
  4. @DirtiesContext, or a different key (a different set of @MockitoBeans, properties or profiles), forces a new context. The cache is bounded (default 32 entries, with LRU eviction).
  5. The test instance's fields are injected with beans. webEnvironment = RANDOM_PORT starts a real embedded server.

Speed tip: standardise the test configuration (a shared abstract base class, or a meta-annotation) so the tests share one cached context.

Q4. What does @SpringBootTest(webEnvironment = …) actually do?

Short answer:

  • MOCK (the default): a mock servlet environment, with no real server. Use it with @AutoConfigureMockMvc and MockMvc. It's fast. The request goes through the DispatcherServlet and filters, but not the network or the servlet container.
  • RANDOM_PORT: starts the real embedded server on a random port. Inject it with @LocalServerPort, and call it with TestRestTemplate/WebTestClient/RestClient. It tests the real HTTP stack (the container's filters, error pages, compression, TLS).
  • DEFINED_PORT: a real server on the configured port (conflict-prone in CI).
  • NONE: no web environment at all (batch jobs, messaging-only applications).

Common trap: with a real server, the test and the server run on different threads, so @Transactional test rollback doesn't cover the server-side work. Clean the data up explicitly.

Q5. How do you test only one layer (a controller, service or repository) in isolation? How do you isolate the service layer for integration testing?

Short answer:

  • Controller: @WebMvcTest(OrderController.class), with the services as @MockitoBean.
  • Repository: @DataJpaTest (with Testcontainers for the real database).
  • Service unit tests: plain JUnit + Mockito, with no Spring (constructor injection makes this trivial). That's the fastest option, and ideal for business rules.
  • Service integration (a real transaction and real repositories, but no web layer): @SpringBootTest(webEnvironment = NONE), or a custom slice (@DataJpaTest + @Import(OrderService.class, …)) with Testcontainers, mocking only external clients (@MockitoBean PaymentClient), or stubbing them with WireMock.

Q6. Why would you use @TestConfiguration?

Short answer: To add or override beans for tests only, without polluting production configuration or component scanning:

  • a fixed Clock (deterministic time);
  • fake or stub implementations of external gateways;
  • test-specific ObjectMapper or security settings;
  • Testcontainers container beans with @ServiceConnection (Boot 3.1+ TestcontainersConfiguration).

As a nested static class, it's picked up automatically by the enclosing test. As a top-level class, import it with @Import. It's excluded from component scanning by default (through TypeExcludeFilter).

Q7. When and why would you use @DirtiesContext? When should you avoid it?

Short answer: @DirtiesContext marks the application context as dirty, so Spring closes it and removes it from the cache after the test method or class (the classMode/methodMode options). Use it when a test mutates shared context state that can't easily be reset:

  • modifies singleton beans;
  • changes caches or static configuration;
  • starts background threads or schedulers;
  • alters an embedded broker's state.

Avoid it in most cases: it destroys context caching, so every dirtied test pays full context start-up, and the suites get dramatically slower. Prefer:

  • resetting state explicitly (@BeforeEach/@AfterEach cleanup, @Sql scripts, truncating tables);
  • designing beans without mutable global state;
  • using @MockitoBean, which resets its mocks automatically after each test.

Q8. What does @Transactional do in test classes? How do you prevent rollback (persist data) during a test? How do you reset database state between tests?

Short answer:

  • @Transactional on a test (or the class) makes the Spring TestContext framework start a transaction before each test method, and roll it back afterwards (@DataJpaTest does this by default). The database stays clean, with no manual cleanup.
  • To keep the data: annotate with @Commit or @Rollback(false). That's useful for debugging, or for verifying behaviour after commit (for example AFTER_COMMIT listeners).
  • The caveats of rollback-based tests:
    • the transaction never commits, so @TransactionalEventListener(AFTER_COMMIT) listeners don't run, deferred constraints aren't checked, and flush-time errors may never happen (call flush(), or test without a transaction);
    • they don't cover work on other threads (@Async, or a real server with RANDOM_PORT);
    • the persistence context may return entities from memory.
  • Other ways to reset state:
    • @Sql scripts (before or after the test method: truncate, insert fixtures);
    • JdbcTestUtils deleteFromTables;
    • a Testcontainers container per class (slow);
    • Flyway clean + migrate per test class (slow but thorough);
    • the Testcontainers "reusable" mode, plus truncation.

Q9. What's the difference between unit tests and integration tests?

Short answer:

  • Unit tests verify one unit (a class or function) in isolation, replacing its collaborators with test doubles. They're very fast (milliseconds), deterministic, pinpoint failures, and are written in large numbers. They cover business rules, edge cases and algorithms.
  • Integration tests verify that components work together with real infrastructure or frameworks: the Spring context, the real database (Testcontainers), serialisation, transactions, the security configuration, messaging, and HTTP clients against stubs. They're slower and fewer, and catch wiring, configuration, query and mapping bugs that unit tests can't.
  • Also:
    • contract tests (consumer-driven, for API compatibility);
    • end-to-end tests (the deployed system, a few critical journeys);
    • performance and chaos tests.

Aim for the testing pyramid (or "honeycomb" for microservices: more integration tests around the service boundaries).

Q10. How do you test conditionally loaded beans (for example, through profiles)?

Short answer:

  • Profile-based beans: activate profiles with @ActiveProfiles("prod") in a context test, and assert on the beans present or absent (assertThat(context).hasSingleBean(...)/doesNotHaveBean(...)).
  • Auto-configuration and @Conditional* logic: ApplicationContextRunner (or WebApplicationContextRunner). It's fast, with no Spring Boot startup overhead, and you declare properties, user configurations and classpath filters per case:
private final ApplicationContextRunner runner = new ApplicationContextRunner()
        .withConfiguration(AutoConfigurations.of(AuditAutoConfiguration.class));

@Test void backsOffWhenUserDefinesClient() {
    runner.withUserConfiguration(CustomClientConfig.class)
          .run(ctx -> assertThat(ctx).hasSingleBean(AuditClient.class).getBean(AuditClient.class).isSameAs(CustomClientConfig.CLIENT));
}
@Test void disabledByProperty() {
    runner.withPropertyValues("acme.audit.enabled=false").run(ctx -> assertThat(ctx).doesNotHaveBean(AuditClient.class));
}
@Test void absentWithoutLibrary() {
    runner.withClassLoader(new FilteredClassLoader(AuditClient.class)).run(ctx -> assertThat(ctx).doesNotHaveBean(AuditAutoConfiguration.class));
}

Q11. How do you control environment properties for integration tests?

Short answer:

  • @SpringBootTest(properties = {"app.feature.x=true"}), or @TestPropertySource (inline properties, or a file). These have high precedence.
  • @DynamicPropertySource: properties computed at runtime (Testcontainers ports and URLs, WireMock base URLs). Largely replaced by @ServiceConnection in Boot 3.1+.
  • application-test.yml with @ActiveProfiles("test").
  • Environment variables, or system properties in CI (they're less explicit, so avoid them for test logic).

Keep each test's properties explicit, and minimal. Every unique property combination creates a separate cached context.

Q12. Can integration tests run in parallel safely?

Short answer: Yes, with care. JUnit 5 supports parallel execution (junit.jupiter.execution.parallel.enabled=true, and mode.default=concurrent). Build tools can also fork JVMs (Maven Surefire forkCount, Gradle maxParallelForks). The risks come from shared state:

  • the same database rows or tables: isolate by using unique data per test (random IDs or tenants), schema- or database-per-fork, or separate Testcontainers per fork;
  • fixed ports (use random ports);
  • static or singleton state, and the shared Spring context mutated by tests;
  • message topics or queues (unique topic names per test);
  • time-dependent tests.

Strategy: run forks in parallel, with isolated infrastructure per fork, and keep tests within a context sequential unless they're proven independent. Use @ResourceLock/@Isolated in JUnit for the exceptions.

Q13. How do you use an embedded database (H2) in integration tests, and should you?

Short answer: @DataJpaTest (by default) or @AutoConfigureTestDatabase replaces the DataSource with an embedded H2/HSQL/Derby instance, when it's on the classpath. Hibernate creates the schema (or Flyway runs). H2's compatibility modes (MODE=PostgreSQL) help a little. The problems:

  • dialect differences: SQL functions, JSON types, locking, isolation semantics, sequences, native queries, index behaviour and constraint handling;
  • tests pass on H2 and fail in production.

Prefer Testcontainers with the same database engine and version as production. Keep H2 only for very simple, portable repository tests, or where Docker isn't available.

Q14. How do you inject security credentials or JWTs into integration tests?

Short answer:

  • MockMvc:
    • @WithMockUser(username = "alice", roles = "ADMIN"), @WithUserDetails, or custom @WithSecurityContext annotations;
    • or request post-processors: .with(jwt().jwt(j -> j.claim("scope", "orders.read")).authorities(...)), .with(oauth2Login()), .with(user("alice").roles("USER")), and .with(csrf()) for form or cookie flows.
  • Full server (RANDOM_PORT): obtain a real token:
    • from a Testcontainers Keycloak (or a mock OAuth2 server like mock-oauth2-server);
    • or sign test JWTs with a test key pair, configuring the resource server with the test public key (spring.security.oauth2.resourceserver.jwt.public-key-location).
    • Pass it as Authorization: Bearer ….
  • Reactive: WebTestClient with mutateWith(mockJwt()).

Never disable security for the whole test suite. Test the authorisation rules explicitly (401, 403, and allowed roles).

Follow-up questions this topic invites — and their answers

Q: How many cached contexts are too many? A: Each context holds its beans, pools and threads in memory, and starting one can take seconds. If the logs show many context starts (look for "Started … in X seconds" repeatedly), consolidate the configurations. Spring 6.2 can pause inactive cached contexts, to reduce resource use.

Q: @MockitoBean vs @MockBean? A: @MockBean (Spring Boot) is deprecated since 3.4 in favour of @MockitoBean/@MockitoSpyBean (Spring Framework 6.2), which live in the core test context framework and support fields in @Nested classes and configurations more consistently.

Q: What is TestEntityManager? A: A test helper in @DataJpaTest that wraps EntityManager with convenient methods (persistAndFlush, find), for setting up data and forcing flushes in repository tests.

Q: How do you test @Scheduled jobs without waiting? A: Extract the job's logic into a bean method, and test it directly. For the scheduling wiring, use a short fixed delay in tests, with Awaitility, or disable scheduling in tests and trigger the method manually.

Previous

Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Next

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

AI Tutor

Lesson: Spring Boot Test Slices, Context & Test Strategy — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.