Testing a Boot app (unit, slice and integration tests), unit testing purpose, JUnit and Mockito, @Mock vs @InjectMocks, @SpringBootTest, global exception handling, pom.xml, auto-configuration and customising or disabling it, the starter parent and starters.
Published September 25, 2026
Testing questions reveal whether you've worked on a real codebase. Describe the test pyramid: fast unit tests with Mockito, focused slice tests, and a few full @SpringBootTest integration tests. Then explain why each layer exists.
Short answer: In layers:
@WebMvcTest for controllers, with MockMvc.@DataJpaTest for repositories, against an embedded database or Testcontainers.@JsonTest for serialisation.@SpringBootTest loads the full context, often with Testcontainers for a real database or Kafka.@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired MockMvc mvc;
@MockitoBean OrderService service; // Boot 3.4+ (older versions: @MockBean)
@Test void returnsOrder() throws Exception {
when(service.find(1L)).thenReturn(new OrderDto(1L, "PAID"));
mvc.perform(get("/api/orders/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("PAID"));
}
}
Learn it in depth → Spring Exception Handling
Short answer: To verify that small units of code (a method or a class) behave correctly in isolation. It catches bugs early, documents the intended behaviour, and gives you the confidence to refactor. Good unit tests are fast, deterministic and independent of each other.
Key points to cover:
Short answer: JUnit 5 is the test framework. It provides @Test, assertions, the lifecycle (@BeforeEach), parameterised tests, and the runner. Mockito creates mock objects for a class's dependencies, so you can stub their behaviour (when(...).thenReturn(...)) and verify interactions (verify(...)) without a real database or HTTP call.
@ExtendWith(MockitoExtension.class)
class CheckoutServiceTest {
@Mock PaymentGateway gateway;
@Mock OrderRepository orders;
@InjectMocks CheckoutService checkout;
@Test void failedPaymentDoesNotSaveOrder() {
when(gateway.charge(any())).thenReturn(PaymentResult.declined("insufficient funds"));
assertThrows(PaymentDeclinedException.class, () -> checkout.placeOrder(sampleCart()));
verify(orders, never()).save(any());
}
}
@Mock and @InjectMocks?Short answer: @Mock creates a fake dependency. @InjectMocks creates the real object under test, and injects the @Mock fields into it, through the constructor, setters or fields.
Key points to cover:
@InjectMocks fails silently when it can't match a dependency: the field is simply left null. With constructor injection, you can instead write checkout = new CheckoutService(gateway, orders) in @BeforeEach, which is explicit and fails loudly.@Spy wraps a real object, where only the stubbed methods are faked. Use it sparingly.@SpringBootTest?Short answer: It starts the full application context: all beans, auto-configuration and properties. That lets you test how components work together. With webEnvironment = RANDOM_PORT, it also starts the embedded server, for real HTTP tests.
Key points to cover:
@MockitoBean setups, which force the context to be rebuilt.@ServiceConnection, Boot 3.1+) for realistic databases and brokers.Short answer: Centralise error handling in a @RestControllerAdvice class, with @ExceptionHandler methods that map exceptions to HTTP responses. Use Problem Details (RFC 9457, ProblemDetail), so that clients get a consistent JSON error format.
@RestControllerAdvice
class ApiExceptionHandler {
@ExceptionHandler(OrderNotFoundException.class)
ProblemDetail notFound(OrderNotFoundException ex) {
ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
pd.setTitle("Order not found");
return pd;
}
@ExceptionHandler(MethodArgumentNotValidException.class)
ProblemDetail invalid(MethodArgumentNotValidException ex) {
ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
pd.setProperty("errors", ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage()).toList());
return pd;
}
}
Key points to cover:
Learn it in depth → Spring Exception Handling
pom.xml in a Maven project?Short answer: It's the Project Object Model. It declares the project's coordinates (groupId, artifactId, version), packaging, dependencies, plugins, build settings, properties and profiles. Maven reads it to download dependencies, compile, test and package the application.
Learn it in depth → Maven Interview Questions
Short answer: It creates the infrastructure beans your app needs, based on what's on the classpath and in your properties.
spring-boot-starter-data-jpa plus a JDBC URL gives you a DataSource, an EntityManagerFactory and a transaction manager.spring-boot-starter-web gives you Tomcat, the DispatcherServlet and Jackson.It all backs off wherever you define your own beans.
Learn it in depth → Auto-Configuration Mechanism
Short answer: Yes, in increasing order of control:
spring.datasource.hikari.maximum-pool-size, spring.jackson.default-property-inclusion).Jackson2ObjectMapperBuilderCustomizer, WebServerFactoryCustomizer), which tweak the auto-configured bean without replacing it.Key points to cover:
@AutoConfiguration plus @Conditional… annotations, registered in the AutoConfiguration.imports file.Short answer: Use @SpringBootApplication(exclude = DataSourceAutoConfiguration.class), or excludeName with a fully qualified class name, or the spring.autoconfigure.exclude property (which can differ per profile).
Learn it in depth → Spring Boot Runners, Servers & Configuration
spring-boot-starter-parent?Short answer: It's a Maven parent POM that provides:
-parameters, surefire, the Spring Boot plugin's repackage goal);application*.yml.Key points to cover:
spring-boot-dependencies in <dependencyManagement> with <scope>import</scope> instead. You get the version management without the parent.Short answer: One starter replaces a list of individually chosen, individually versioned dependencies. For example, spring-boot-starter-web brings Spring MVC, Jackson, validation support and embedded Tomcat. It also triggers the matching auto-configuration, so adding a dependency is often all it takes to enable a feature.
Q: What is Testcontainers, and why use it? A: A library that starts real dependencies (PostgreSQL, Kafka, Redis) in Docker containers during tests. You test against the real database, rather than an in-memory stand-in with different SQL behaviour.
Q: @MockBean vs @Mock?
A: @Mock (Mockito) creates a mock for a plain unit test. @MockBean (now @MockitoBean) replaces a bean inside the Spring context with a mock, so it's used in slice and integration tests.
Q: How do you test a repository?
A: With @DataJpaTest. It configures JPA, repositories and an embedded or Testcontainers database, and rolls back after each test. Test your custom queries there, not the built-in CRUD methods.
Q: What does @Transactional on a test do?
A: Each test runs in a transaction that is rolled back at the end, which keeps tests isolated. Be careful: rollback can hide problems that only appear on commit, such as constraint checks or flush-time errors.