Skip to content

Slice Testing Interview Questions & Answers

15 questions Updated 2026-06-26 Share:

Spring Boot test slices — @WebMvcTest with MockMvc, @DataJpaTest with TestEntityManager and an embedded DB, @JsonTest, @RestClientTest, @MockBean for slice collaborators, and how slices keep the context small and fast.

Read the in-depth guideSpring Boot Test Slices: @WebMvcTest, @DataJpaTest, and Friends(opens in new tab)
15 of 15

A test slice loads only the part of the context relevant to one layer instead of the whole application. @WebMvcTest loads the web layer, @DataJpaTest the persistence layer, @JsonTest the serialization layer — each registers only the beans that layer needs.

@WebMvcTest(OrderController.class)   // controllers + MVC infra only
@DataJpaTest                         // repositories + JPA + embedded DB
@JsonTest                            // Jackson + JSON beans

Because a slice skips your services, security config, schedulers, etc., it starts far faster than @SpringBootTest while still testing real framework behavior (routing, JSON binding, SQL). It's the middle tier of the test pyramid: more realistic than a unit test, lighter than a full integration test.

Rule of thumb: Use a slice when you want real framework wiring for one layer without paying to boot the entire application.

@WebMvcTest boots only the web layer — controllers, @ControllerAdvice, filters, converters, and an auto-configured MockMvc — but not @Service/@Repository/@Component beans. You provide collaborators as mocks.

@WebMvcTest(OrderController.class)
class OrderControllerTest {
    @Autowired MockMvc mvc;
    @MockBean OrderService service;     // controller's dependency, mocked

    @Test void returnsOrder() throws Exception {
        when(service.find(1L)).thenReturn(new Order(1L, "PAID"));
        mvc.perform(get("/orders/1"))
           .andExpect(status().isOk())
           .andExpect(jsonPath("$.status").value("PAID"));
    }
}

It tests routing, request mapping, validation, serialization, and exception handling without a server or a database. Naming the controller (@WebMvcTest(OrderController.class)) keeps the slice to a single controller.

Rule of thumb: @WebMvcTest + MockMvc is the standard way to test controller behavior; mock the services it calls with @MockBean.

MockMvc drives the Spring MVC DispatcherServlet in-process — no Tomcat, no socket, no real HTTP. It builds a mock request, runs it through the full MVC machinery (mapping, argument resolution, the controller, exception handling, view/JSON rendering), and lets you assert on the result.

mvc.perform(post("/orders")
        .contentType(MediaType.APPLICATION_JSON)
        .content("{\"item\":\"widget\"}"))
   .andExpect(status().isCreated())
   .andExpect(header().exists("Location"))
   .andExpect(jsonPath("$.id").isNumber());

Because it skips the network it's fast and deterministic, but it does not exercise the servlet container, real connection handling, or HTTP-level concerns. For those you need a running server (@SpringBootTest(webEnvironment = RANDOM_PORT) + TestRestClient/WebTestClient).

Rule of thumb: Use MockMvc to test controller logic fast; use a real port only when you must verify true HTTP/container behavior.

By design. A slice uses a filtered component scan that includes only beans relevant to its layer — @Controller, @ControllerAdvice, Converter, Filter, Jackson modules — and excludes @Service, @Repository, and ordinary @Components. That's what keeps the context tiny.

@WebMvcTest(OrderController.class)
class T {
    @MockBean OrderService service;   // REQUIRED — not scanned, you supply it
}

If you forget to provide a collaborator the controller needs, the context fails with "No qualifying bean of type ... OrderService". The slice expects you to mock the layers below with @MockBean.

Rule of thumb: In a @WebMvcTest, every service the controller depends on must be a @MockBean — the slice deliberately leaves them out.

@DataJpaTest boots the persistence slice: JPA @Entity classes, Spring Data repositories, EntityManager, a TestEntityManager, and — by default — an in-memory embedded database. It does not load services or controllers.

@DataJpaTest
class OrderRepositoryTest {
    @Autowired OrderRepository repo;
    @Autowired TestEntityManager em;

    @Test void findsByStatus() {
        em.persist(new Order("PAID"));
        assertThat(repo.findByStatus("PAID")).hasSize(1);
    }
}

It's also @Transactional by default, so each test rolls back at the end — the database is clean between tests. Ideal for verifying custom queries, derived methods, mappings, and constraints against a real SQL engine.

Rule of thumb: Use @DataJpaTest to test repositories and JPA mappings against a real (embedded) database, with automatic rollback keeping tests isolated.

TestEntityManager is a test-scoped wrapper around the JPA EntityManager for arranging and inspecting data without going through the repository you're testing. Using the repo to set up its own test can hide bugs (a broken save would mask a broken find).

Long id = em.persistAndGetId(new Order("PAID"), Long.class); em.flush();   // force SQL now em.clear();   // detach so the next read hits the DB, not the cache
Order found = repo.findById(id).orElseThrow();   // exercises the repo ```

`persistAndFlush`, `flush`, and `clear` let you control exactly what's in the DB vs. the persistence context — crucial for tests that must read fresh from the database rather than the first-level cache.

**Rule of thumb:** Set up and verify with `TestEntityManager`; exercise the **repository** with the call you're actually testing.

By default @DataJpaTest replaces any configured datasource with an embedded one (H2/HSQLDB). Disable that replacement to test against the real engine — important because H2 and Postgres differ in SQL dialect and constraints.

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class OrderRepositoryIT {
    @Container @ServiceConnection
    static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16");
    // repo tests run against real Postgres
}

Testing on H2 risks "passes locally, fails in prod" when you use Postgres-specific types, JSON columns, or native queries. Replace.NONE plus Testcontainers gives a real database while keeping the slim slice.

Rule of thumb: H2 is convenient but not your prod DB — for query-heavy or dialect-specific repositories, use @AutoConfigureTestDatabase(replace = NONE) with Testcontainers.

@JsonTest boots just the JSON serialization slice — Jackson ObjectMapper, configured modules, and test helpers — to verify your DTOs serialize and deserialize exactly as expected, including field names, date formats, and @JsonIgnore.

@JsonTest
class OrderJsonTest {
    @Autowired JacksonTester<Order> json;

    @Test void serializes() throws Exception {
        var result = json.write(new Order(1L, "PAID"));
        assertThat(result).hasJsonPathValue("$.status");
        assertThat(result).extractingJsonPathStringValue("$.status").isEqualTo("PAID");
    }

    @Test void deserializes() throws Exception {
        var order = json.parseObject("{\"id\":1,\"status\":\"PAID\"}");
        assertThat(order.getStatus()).isEqualTo("PAID");
    }
}

It uses your real, app-configured ObjectMapper, so it catches mapping issues a hand-rolled mapper in a unit test would miss (custom serializers, naming strategy, modules).

Rule of thumb: Use @JsonTest + JacksonTester to lock down the exact JSON shape of API DTOs with the application's real Jackson configuration.

@RestClientTest slices in just the beans needed to test a client that calls an external API — your RestClient/RestTemplate/WebClient builder plus a MockRestServiceServer that stubs the remote endpoint's responses.

@RestClientTest(WeatherClient.class)
class WeatherClientTest {
    @Autowired WeatherClient client;
    @Autowired MockRestServiceServer server;

    @Test void parsesResponse() {
        server.expect(requestTo("/weather?city=NYC"))
              .andRespond(withSuccess("{\"temp\":21}", MediaType.APPLICATION_JSON));
        assertThat(client.tempFor("NYC")).isEqualTo(21);
    }
}

It tests your outbound call — URL building, headers, response parsing, error handling — without hitting the real third party. The mock server also lets you simulate timeouts and 5xx to test your client's resilience.

Rule of thumb: Use @RestClientTest with MockRestServiceServer to test API clients deterministically, without depending on the live remote service.

@MockBean adds (or replaces) a bean in the slice's context with a Mockito mock. In a @WebMvcTest you use it to supply the services the controller calls, since the slice doesn't scan them; you then stub the mock as usual.

@WebMvcTest(OrderController.class)
class T {
    @Autowired MockMvc mvc;
    @MockBean OrderService service;

    @Test void ok() throws Exception {
        when(service.find(1L)).thenReturn(new Order(1L, "PAID"));
        mvc.perform(get("/orders/1")).andExpect(status().isOk());
    }
}

Each distinct set of @MockBeans produces a different context configuration, which Spring caches separately — too many unique combinations defeats context caching and slows the suite. (Boot 3.4+ introduces @MockitoBean as the replacement for the now-deprecated @MockBean.)

Rule of thumb: @MockBean is how slices get their collaborators — keep the set of mocked beans consistent across tests so the cached context is reused.

Use a slice when you're testing one layer in isolation and want speed; use @SpringBootTest when you need multiple layers wired together (controller → service → repository → DB) end to end.

@WebMvcTest      → controller behavior, services mocked          (fast)
@DataJpaTest     → repository/query behavior, embedded DB        (fast)
@SpringBootTest  → real flow across all layers                   (slow)

Slices keep the context tiny and the feedback loop tight, so they should be the majority of your Spring tests; full @SpringBootTest runs are valuable but expensive, so keep them few. This is the test pyramid applied to Spring.

Rule of thumb: Default to the narrowest slice that covers the behavior; escalate to @SpringBootTest only when a test genuinely spans layers.

@WebMvcTest does apply your Spring Security filter chain, so an unauthenticated request can get a 401/403 and fail your test. Use Spring Security's test support to supply a user and CSRF tokens.

@WebMvcTest(OrderController.class)
class T {
    @Autowired MockMvc mvc;
    @MockBean OrderService service;

    @Test
    @WithMockUser(roles = "ADMIN")
    void adminCanDelete() throws Exception {
        mvc.perform(delete("/orders/1").with(csrf()))   // csrf() post-processor
           .andExpect(status().isNoContent());
    }
}

@WithMockUser injects an authenticated principal; .with(csrf()) supplies a valid CSRF token for state-changing methods. Requires spring-security-test on the classpath. (You can also use .with(user("u").roles("ADMIN")) per request.)

Rule of thumb: In @WebMvcTest, authenticate with @WithMockUser and add .with(csrf()) to POST/PUT/DELETE, or the security chain will reject the request.

Use jsonPath(...) expectations, which evaluate JSONPath expressions against the response body — far cleaner than string-matching the raw JSON.

mvc.perform(get("/orders/1"))
   .andExpect(status().isOk())
   .andExpect(jsonPath("$.id").value(1))
   .andExpect(jsonPath("$.status").value("PAID"))
   .andExpect(jsonPath("$.items").isArray())
   .andExpect(jsonPath("$.items.length()").value(3))
   .andExpect(jsonPath("$.secret").doesNotExist());

$ is the root; $.field reads a property; $.arr[0] indexes; .doesNotExist() asserts a field is absent (e.g. a masked password). You can also pull the body out with andReturn() and parse it with JacksonTester for richer assertions.

Rule of thumb: Assert response bodies with jsonPath, and use .doesNotExist() to prove sensitive fields are not leaked.

The recurring traps:

  • Using @SpringBootTest when a slice would do — slow tests for no extra coverage.
  • Forgetting a @MockBean for a controller dependency → "No qualifying bean" failure.
  • Assuming @WebMvcTest ignores security — it doesn't; add @WithMockUser/csrf().
  • Trusting H2 in @DataJpaTest for Postgres-specific SQL → green locally, red in prod.
  • Setting up @DataJpaTest data through the repo under test instead of TestEntityManager.
  • Spawning many unique @MockBean combinations, busting the context cache.
// Forgetting the mock — context won't start:
@WebMvcTest(OrderController.class)
class Broken { @Autowired MockMvc mvc; }   // missing @MockBean OrderService

Rule of thumb: Pick the narrowest slice, mock its collaborators, remember security applies, and don't trust H2 for dialect-specific queries.

Slices deliberately omit unrelated auto-config, but you can opt specific pieces back in with the @AutoConfigure... annotations, or import a specific config with @Import.

@WebMvcTest(OrderController.class)
@Import(JacksonConfig.class)                 // bring in a custom @Configuration
@AutoConfigureMockMvc(addFilters = false)    // skip security filters if desired
class T { /* ... */ }

For example @AutoConfigureMockMvc(addFilters = false) disables the security chain in a web slice; an @Import pulls in a converter or @ControllerAdvice that isn't component-scanned by the slice. This lets you widen a slice just enough without escalating to a full @SpringBootTest.

Rule of thumb: Widen a slice surgically with @Import / @AutoConfigure... rather than jumping to @SpringBootTest the moment something's missing.

More ways to practice

The self-quiz is live. Join our channel for updates, new content & tech tips.

Join our WhatsApp Channel