skip to content

Why is @DataJpaTest faster than @SpringBootTest, and what exactly is excluded from the slice?

level: seniorimportance: should knowfreq 58%

answer

  1. Curated auto-config + no full component scan
  2. Excludes services/controllers/security/MVC
  3. No web server, embedded DB
  4. TestContext context caching by config key
  5. @Import / @MockBean to add collaborators

basics

~10 s

@DataJpaTest only auto-configures JPA/persistence beans and disables full component scanning, so the context is small and starts fast. @SpringBootTest loads the entire application — controllers, services, security, web server — which is much heavier.

solid answer

~40 s

A test slice like @DataJpaTest limits auto-configuration to a curated set (via @ImportAutoConfiguration/@TypeExcludeFilters) and turns off regular @ComponentScan, so only @Entity classes, Spring Data repositories, EntityManager, DataSource, and TestEntityManager get created. @Service, @Component, @Controller, Spring MVC, and Security are excluded. That means a much smaller bean graph, no embedded web server, and — combined with the default embedded in-memory DB — a fast, isolated context. @SpringBootTest instead loads the full @SpringBootApplication context (optionally a real servlet container), so it's slower but higher-fidelity end-to-end. A practical bonus: Spring's TestContext framework caches contexts by configuration, so many @DataJpaTest classes sharing the same slice config reuse one cached context, amortizing startup cost across the suite.

code

java · 14 lines
java
// Slice: tiny context, cached and reused across all @DataJpaTest classes
@DataJpaTest
class ProductRepositoryTest {
    @Autowired ProductRepository repo;      // available
    // @Autowired PricingService pricing;   // WOULD FAIL: not in the slice
}

// Need a collaborator? Import just that one, don't widen to @SpringBootTest
@DataJpaTest
@Import(SlugGenerator.class)
class ArticleRepositoryTest {
    @Autowired ArticleRepository repo;
    @Autowired SlugGenerator slugGenerator; // now available via @Import
}

go deeper

for a junior

Know it loads less than @SpringBootTest so it starts faster.

for a middle

List concretely what's excluded (services/controllers/web/security) and how to add a single collaborator via @Import/@MockBean.

for a senior

Explain the slice mechanism (curated auto-config + type-exclude filters + no component scan) and the context-caching win.

for a principal

Guide a testing strategy: many fast slices plus a few full @SpringBootTest integration tests, and protect context-cache reuse across the suite.

## Mechanism: how a slice narrows the context `@DataJpaTest` is composed of several meta-annotations that constrain what the context contains: - **`@ImportAutoConfiguration` / a curated auto-config group** — instead of Spring Boot's full auto-configuration, only persistence-relevant auto-configurations are applied (JPA, DataSource, transaction, embedded DB). - **`@TypeExcludeFilters(DataJpaTypeExcludeFilter.class)`** — filters out beans that don't belong to the slice. - **Disabled full `@ComponentScan`** — the slice does **not** scan and register your `@Service`/`@Component`/`@Controller` beans. Only `@Entity` classes and Spring Data repository interfaces are picked up. So the resulting bean graph is roughly: DataSource → EntityManagerFactory → repositories → TestEntityManager + transaction manager. No web server, no MVC dispatcher, no security filter chain, no business services. ## Why that's faster - **Fewer beans to instantiate** → less reflection, fewer proxies, faster refresh. - **No embedded servlet container** started (unlike a `@SpringBootTest(webEnvironment = ...)` with a real port). - **No heavy auto-configurations** (messaging, caching, mail, security, etc.). - **Embedded in-memory DB** starts in milliseconds vs. connecting to an external DB. ## Context caching (the big multiplier) Spring's **TestContext framework caches** the `ApplicationContext` keyed by its configuration (annotations, active profiles, `@MockBean` set, etc.). All `@DataJpaTest` classes that share identical slice configuration reuse **one** cached context across the whole test run — you pay startup once. Adding a `@MockBean` or `@TestPropertySource` changes the key and creates a *separate* cached context, so gratuitous per-class customization fragments the cache and slows the suite. ## What's excluded (know this cold) - `@Service`, `@Component`, `@Repository` (your own non-Spring-Data ones as plain components), `@Controller`/`@RestController`. - Spring MVC infrastructure, `@RestControllerAdvice`, filters. - Spring Security filter chain. - Custom `@Configuration` unless imported. ## Bringing excluded things in - **`@Import(SomeConfig.class)`** or a nested `@TestConfiguration` to add a specific collaborator. - **`@MockBean`** to stub a dependency the repository test needs. - Fall back to **`@SpringBootTest`** if you genuinely need the full stack (integration test across layers). ## When to choose which - **`@DataJpaTest`**: query correctness, mappings, constraints — narrow, fast, many of them. - **`@SpringBootTest`**: end-to-end flows, wiring across controllers→services→repositories, real HTTP. ## Gotchas - Expecting a `@Service` to be autowired in a slice → `NoSuchBeanDefinitionException`. - Over-customizing each slice test with unique `@TestPropertySource`/`@MockBean`, destroying context-cache reuse. - Assuming slice = full isolation from other config: an `@Import`ed config can pull in more than intended.

  • How does Spring avoid restarting the context for every @DataJpaTest class?
    The TestContext framework caches contexts keyed by their configuration. Classes with identical slice config share one cached context, so startup cost is paid once. Adding @MockBean/@TestPropertySource changes the key and forks a new cached context.
  • Your repository test needs one @Service method. What's the lightest way to get it without @SpringBootTest?
    Bring in just that bean with @Import(TheService.class) or a nested @TestConfiguration, or stub it with @MockBean — keeping the slice small instead of loading the whole app.
  • Does @DataJpaTest start an embedded web server?
    No. There's no servlet container, MVC dispatcher, or security filter chain — that's part of why it's fast and why controller-level code can't be exercised in it.

saying these in an interview costs you the question

  • Claiming @DataJpaTest loads the full application just with a smaller DB
  • Thinking services/controllers are available in the slice
  • Not knowing about context caching / thinking each class restarts everything
  • Over-customizing every slice test and fragmenting the context cache

context