Interview Prep Zoneby CuriouserLabs

Question 1 of 5 · senior level

@SpringBootTest vs test slices (@WebMvcTest, @DataJpaTest) — how do you structure a service's test suite?

🎤 Say this first

Shape it as a pyramid with explicit jobs: plain unit tests (no Spring) for domain logic — the bulk; slice tests for framework-boundary behavior — @WebMvcTest (controllers + MVC infra: serialization, validation, error mapping, security rules) and @DataJpaTest (repositories + real SQL against Testcontainers); a thin layer of @SpringBootTest integration tests for wiring and end-to-end flows; and a few smoke tests against deployed environments. The failure mode to avoid: everything-as-@SpringBootTest — minutes-long suites, flaky, and failures that don't localize.

The full picture

Each layer tests what only it can test
// 1. Unit — fast, no Spring: the RULES
class PricingPolicyTest {
    @Test void bulkDiscountAppliesOver100Units() {
        assertThat(new PricingPolicy().price(order(150)))
            .isEqualTo(Money.of("1350.00"));
    }
}

// 2. Slice — the CONTRACT at the web boundary
@WebMvcTest(OrderController.class)
class OrderControllerTest {
    @Autowired MockMvcTester mvc;
    @MockitoBean OrderService service;       // collaborators mocked
    @Test void invalidBodyYields422WithProblemDetail() {
        assertThat(mvc.post().uri("/orders").contentType(APPLICATION_JSON)
                      .content("{}"))
            .hasStatus(422)
            .bodyJson().extractingPath("$.violations").isNotEmpty();
    }
}

// 3. Integration — the WIRING, real infra
@SpringBootTest(webEnvironment = RANDOM_PORT)
@Testcontainers
class OrderFlowIT { … }
  • What slices load: @WebMvcTest = controllers, converters, advice, security filters — no services/repos; @DataJpaTest = repositories, EntityManager, a transaction rolled back per test — no web. Fast because small; honest because they use the real framework plumbing you'd otherwise mock badly.
  • What only @SpringBootTest catches: bean wiring mistakes, property binding, security config integration, cross-cutting behavior (transactions + events + listeners together).
  • Ratio heuristic: if the suite takes >5 min, count your @SpringBootTests; if domain rules are tested through HTTP, your unit layer is missing.
  • Don't test the framework: asserting that Spring injects a bean or that JPA saves an entity is testing Spring, not your code — test your mappings, queries, and rules.

🔄 Likely follow-up questions

  • What belongs in @WebMvcTest that people wrongly put in unit tests? (serialization! validation! error shapes!)
  • Why does @DataJpaTest use rollback, and when does that hide bugs?
  • How do you test security rules per-endpoint without a full context?