Articles
Spring Boot Testing Interview Questions: Test Slices, MockMvc, and Database Boundaries
Practice Spring Boot testing interview questions on test slices, MockMvc, database persistence, rollback behavior, and choosing the right test boundary.
Spring Boot testing interview questions often sound like annotation trivia: when would you use @WebMvcTest, @DataJpaTest, or @SpringBootTest? The useful answer is about the boundary each test includes and the claim it can support. A web slice can prove request mapping and response behavior. It cannot, by itself, prove that a production database committed the right record.
The workbook frames this topic around test-context differences and verifying actual persistence. That is narrower than a general Spring Boot guide.
Key takeaways
- Pick the smallest test boundary that proves the behavior you care about. - @WebMvcTest focuses on Spring MVC components. Collaborators are commonly supplied as mocks or imports; the test does not automatically prove database behavior. - @DataJpaTest focuses on JPA entities and repositories. Its default test transaction is rolled back, which is useful for isolation but can hide assumptions about commit-time behavior. - @SpringBootTest loads a broader application context. It is a wiring test, not a synonym for a realistic production environment. - A successful save() call is not enough evidence of durable persistence. Flush, clear the persistence context, and read through the intended path; for commit-dependent behavior, ensure the transaction really commits. - A test that passes locally can still be misleading if it uses an embedded database while production uses a different engine, dialect, collation, isolation level, or migration history.
What are Spring Boot test slices, in plain English?
A test slice starts only the part of the application that the test needs, plus relevant auto-configuration. That gives faster feedback and makes the test's scope easier to understand. The trade-off is that components outside the slice are absent unless explicitly included.
For example, @WebMvcTest(UserController.class) is a focused way to test the HTTP layer: request mapping, JSON conversion, validation, exception handling, and selected security behavior. The controller's service dependency is usually replaced by a test double. A test that mocks UserService can prove that a request is translated into the expected call and response. It cannot prove that the service's repository query is correct.
@DataJpaTest is aimed at the persistence layer. It configures entities and repository support, and tests are transactional by default in the documented setup. That is convenient: each test can make database changes and have them rolled back. But the annotation does not load every regular application component, and a test's rollback is not the same thing as an application transaction committing successfully.
@SpringBootTest loads the application's broader configuration. Use it when the question is specifically about integration between components or application startup wiring. It costs more time, and a large context can make failures harder to localize. Avoid choosing it simply because it feels more realistic.
How do you choose between @WebMvcTest, @DataJpaTest, and @SpringBootTest?
Start from the behavior under test, not from the annotation you remember. Ask: which boundary could contain the bug, and what observable outcome would convince me it is fixed?
| Test type | Good question to ask | What it can show | What it does not establish by itself |
|---|---|---|---|
| @WebMvcTest with MockMvc | Does this endpoint validate input and return the correct status and JSON? | MVC routing, serialization, validation, controller advice, selected filters | Repository correctness, real database commit, deployed network behavior |
| @DataJpaTest | Does this mapping or repository operation behave correctly? | Entity mapping, repository queries, constraints supported by the configured test database | Full application wiring, production database parity, external side effects |
| @SpringBootTest | Does this application configuration wire the components together? | Broad bean wiring, integration among application layers, selected end-to-end behavior | Production topology or performance unless those are deliberately represented |
| Full HTTP test against a real database | Does the public request result in the intended durable state? | Request-to-persistence path and commit behavior, within the test environment | Cloud networking, production data volume, every operational failure mode |
Suppose an endpoint accepts a new customer, returns 201 Created, and writes a row. Split the contract into questions. Does invalid JSON return 400? That belongs naturally in an MVC test. Does a repository enforce a unique email? Test the persistence behavior. Does the complete application expose the endpoint and wire the service and repository correctly? Use an integration test. Does the application commit the row with the production database's collation and constraints? Run the relevant test against that database engine or a faithful test instance.
The strongest interview answer usually includes a small test pyramid: many fast tests close to the logic, focused slice tests at framework boundaries, and a smaller number of broader integration tests for risks that cross boundaries. The exact proportions depend on the system; do not invent a universal percentage.
What should a MockMvc test prove?
MockMvc exercises Spring MVC request handling without requiring a live network server. A focused test can send an HTTP-like request through the MVC infrastructure and assert status, headers, and response content. That makes it useful for contracts that clients depend on.
For a POST /users endpoint, meaningful assertions could include:
- A valid request returns 201 and a response containing a stable identifier. 2. A missing required field returns the documented validation error shape. 3. A service exception is translated to the intended status without leaking internal details. 4. An unauthenticated request is rejected if the endpoint is protected. 5. A caller lacking permission cannot create a resource for another tenant.
If the service is mocked, verify only the interaction that matters. Over-verifying every internal call makes the test brittle: a harmless refactor can break the test while preserving the endpoint contract. For a security test, be explicit about whether the test is checking filter-chain behavior or only controller logic. A controller test with security filters disabled cannot support a claim that authorization works.
Also remember that MockMvc is not a browser and does not open a TCP connection to an embedded server. If the risk is connector configuration, an actual server port, proxy headers, or network serialization, choose a test that crosses that boundary. The interview signal is knowing why the lighter test is sufficient for one claim and insufficient for another.
How do you verify that data really persisted?
Persistence tests can pass while failing to prove durability. ORM sessions maintain an in-memory persistence context. After saving an entity, reading it back from the same context may simply return the managed object; it does not necessarily demonstrate that the database contains the expected row.
A useful repository test follows a clear sequence:
- Arrange a minimal entity with the relevant fields. 2. Save it using the repository operation under test. 3. Flush pending SQL so database constraints and statements execute. 4. Clear the persistence context so a later read cannot reuse the same managed entity. 5. Query again by the expected key and assert persisted values.
This proves more than checking the object returned by save(). It still does not prove that the transaction committed if the test framework will roll it back at the end. If the production requirement depends on commit-time callbacks, deferred constraints, transaction synchronization, or visibility from another transaction, write a test that exercises that boundary deliberately.
Consider a registration service that writes a user and schedules a welcome message. If the message is sent before the database commits, a later rollback can leave the user with an email for an account that does not exist. The design may use an after-commit hook or an outbox, depending on delivery guarantees. A test should verify the side effect does not happen when the transaction rolls back and does happen under the intended committed path. Do not claim that a transactional test proves production commit behavior when the test itself always rolls back.
When should you use a real database instead of an embedded one?
An embedded database is fast and useful for many repository tests. It is not automatically equivalent to the production database. SQL dialects differ. So can uniqueness semantics, case sensitivity, JSON support, generated columns, locking behavior, identity generation, and transaction isolation.
Use the real engine when the risk depends on those differences. Examples include a query using vendor-specific SQL, a migration that changes an index, a concurrency bug involving row locks, or a collation-sensitive username lookup. A containerized database can make this repeatable, but keep the test's purpose narrow and make its startup and cleanup reliable.
Do not move every unit test to a database container. The goal is risk coverage, not realism for its own sake. Keep pure business rules in plain tests, use slices for framework and persistence behavior, and reserve engine-faithful integration tests for engine-specific claims.
When a test fails only in CI, compare the engine version, migration path, timezone, locale, environment variables, and transaction settings before changing the assertion. A test database created directly from entities may not exercise the same schema as one created through production migrations. If schema evolution is important, run migrations in the test path.
What are common Spring Boot testing mistakes?
Using a full context for a small controller rule. This slows feedback and makes the failing boundary vague. Begin with a slice unless the wiring itself is part of the risk.
Assuming a slice contains every bean. Slices intentionally limit configuration. Import only the necessary collaborators and explain why they belong in this test.
Mocking the component whose behavior you need to prove. If the test mocks the repository, it cannot verify the repository query or mapping.
Reading after save without flushing or clearing. The persistence context can return the same in-memory object and conceal a database problem.
Treating rollback as commit coverage. A rollback-at-end test is excellent for isolation. It does not verify effects that occur only after a successful commit.
Testing with the wrong engine for a database-specific guarantee. An embedded database can accept a query or comparison that production handles differently.
Asserting implementation details instead of contracts. A test should fail when the user-visible behavior or data invariant breaks, not whenever a private method is renamed.
How would you answer a Spring Boot testing interview exercise?
Use this four-part answer:
- Name the contract. “This endpoint must reject a duplicate email and leave the database unchanged.” 2. Choose the narrowest boundary. Use an MVC slice for status and error JSON, and a database-backed test for uniqueness and transaction outcome. 3. Make the assertion independent. Flush and clear before querying persisted state; check the database constraint if that is the guarantee. 4. State the gap. “This does not test production networking; if proxy or connector behavior is in scope, I would add a server-level integration test.”
That answer shows you understand what the framework provides and what remains your responsibility. It also prevents the common trap of saying “I would use @SpringBootTest for everything.”
A practical preparation drill
Take one endpoint you know and write three tests at different boundaries:
- MVC: invalid input returns the expected error response. - Persistence: a unique key is enforced, and a read after flush/clear sees the stored value. - Integration: the application context wires the controller, service, and repository for one end-to-end request.
Then break each test deliberately. Remove the validation annotation, remove the database constraint, or exclude a configuration class. Confirm that the matching test catches the regression and unrelated tests remain understandable. This exercise helps you explain not only how to write tests, but why each test exists.
Sources and Further Reading
- Spring Boot testing reference - Testing Spring Boot applications - Spring Boot @WebMvcTest API - Spring Framework testing reference
Research checked September 24, 2026. The official Spring pages currently reflect Spring Boot 4.x documentation. Test-slice modules and package names can differ across major versions; match the guide to the version named in the role or codebase.