Testing and Production Development· 40 min

JUnit, Mockito, and Integration Tests

Test Java behavior at unit, web, repository, and integration boundaries.

Java interview lesson
Question 27 · Not reviewed

Testing · test pyramid

Can you explain this clearly in an interview?

Use the strong answer as your reference, explain it aloud in your own words, then choose how soon you want to review it again.

Interview prompt

JUnit, Mockito, and Integration Tests

Explain it aloud in your own words

Strong answer

A healthy Java test suite has many fast unit tests for domain rules, focused slice tests for framework boundaries, and a smaller number of integration tests that prove the wiring works. JUnit 5 provides the lifecycle, assertions, and parameterized tests; Mockito replaces collaborators you do not own or that are slow, but mocks should verify behavior at a boundary rather than restate the implementation. Spring slices such as @WebMvcTest and @DataJpaTest load only the relevant part of the context.

Key points

  • Test behavior through public APIs, not private methods or internal call order.
  • @WebMvcTest loads controllers, advice, and filters; collaborators are provided with @MockitoBean (formerly @MockBean).
  • @DataJpaTest is transactional and rolls back by default; use a real database engine for query fidelity.
  • Avoid over-mocking: if a test mocks five collaborators, the design or the test boundary is probably wrong.

Interviewer looks for

Explain which test type you choose for a given risk, how you keep tests deterministic (clocks, randomness, time zones), and when a mock gives false confidence.

Java example

1@WebMvcTest(OrderController.class)
2class OrderControllerTest {
3
4  @Autowired MockMvc mvc;
5  @MockitoBean OrderService service;
6
7  @Test
8  void rejectsInvalidQuantity() throws Exception {
9    mvc.perform(post("/orders")
10          .contentType(MediaType.APPLICATION_JSON)
11          .content("{\"sku\":\"A-1\",\"quantity\":0}"))
12       .andExpect(status().isBadRequest())
13       .andExpect(jsonPath("$.errors[0].field").value("quantity"));
14
15    verifyNoInteractions(service);
16  }
17}
18
19@ParameterizedTest
20@CsvSource({"100, 0", "1000, 50", "5000, 500"})
21void appliesDiscountTiers(int subtotal, int expectedDiscount) {
22  assertEquals(expectedDiscount, pricing.discountFor(subtotal));
23}

Topic interview questions

Revise this topic like an interviewer

These questions are tied to this lesson. Read each question aloud, then compare your explanation with the strong answer.

5 questions
Q1Basic

What is the difference between a unit test, a slice test, and an integration test in Spring?

Strong answer

A unit test exercises a class directly without Spring. A slice test such as @WebMvcTest or @DataJpaTest starts only one layer of the context. An integration test, often @SpringBootTest, starts the full application with real infrastructure to verify wiring and end-to-end behavior.

Q2Intermediate

When should you use a mock versus a real object?

Strong answer

Use a real object for value types and pure domain logic. Mock or fake collaborators that are slow, nondeterministic, or external, such as payment gateways or clocks. Prefer fakes or real infrastructure when the interaction itself, like a SQL query, is what you need to prove.

Q3Intermediate

What is the difference between @Mock, @Spy, and @MockitoBean?

Strong answer

@Mock creates a Mockito mock with default stubbed returns. @Spy wraps a real object so unstubbed calls run real code. @MockitoBean, the successor to @MockBean, replaces a bean in the Spring application context with a mock for that test.

Q4Basic

How do you test code that depends on the current time?

Strong answer

Inject a java.time.Clock instead of calling Instant.now() directly, then pass Clock.fixed or a mutable test clock in tests. This makes expiry, scheduling, and date-boundary behavior deterministic.

Q5Advanced

Why can a @DataJpaTest pass on H2 but fail in production?

Strong answer

H2 differs from PostgreSQL or MySQL in SQL dialect, locking, JSON types, sequences, and constraint behavior, so native queries and edge cases may behave differently. Run repository tests against the same engine and version as production, typically via Testcontainers.

How well do you know this?

Your review choice is saved locally and synced to your learner profile when signed in.