What Spring Testing Covers on the Exam
Testing in Spring is not about memorizing every assertion method in JUnit. On the 2V0-72.22 exam, the testing section is about choosing the right test level and understanding what Spring Boot loads for each annotation.
The exam expects you to recognize the difference between a plain unit test, a Spring test slice, and a full integration test. Most questions are scenario-based: given a controller, repository, profile, mock, or database dependency, which annotation should you use and what beans will be available?
Testing carries roughly 14% of the exam, the same weight as Data Management. Expect questions on @SpringBootTest, MockMvc, @WebMvcTest, @DataJpaTest, @MockBean, @ActiveProfiles, test properties, and transactional rollback behavior.
By the end of this guide, you should be able to answer:
- Whether a test loads the full application context or only one layer.
- Whether a real HTTP server starts.
- When
MockMvcis auto-configured. - Why a controller test fails because a service bean is missing.
- Whether a database test rolls back after each method.
The Spring Testing Pyramid
Spring tests fall into three practical levels:
| Test Type | Spring Context | Speed | Best For |
|---|---|---|---|
| Plain unit test | No | Fastest | Business logic with no framework behavior |
| Slice test | Partial | Fast | One Spring layer, such as MVC or JPA |
@SpringBootTest | Full | Slowest | End-to-end application wiring and integration behavior |
A plain unit test uses JUnit and Mockito without starting Spring:
class PriceCalculatorTest {
private final DiscountPolicy discountPolicy = mock(DiscountPolicy.class);
private final PriceCalculator calculator = new PriceCalculator(discountPolicy);
@Test
void appliesDiscount() {
when(discountPolicy.discountFor("SPRING")).thenReturn(BigDecimal.TEN);
BigDecimal result = calculator.priceFor("SPRING");
assertThat(result).isEqualTo(new BigDecimal("90.00"));
}
}No ApplicationContext, no component scanning, no auto-configuration, no profiles. This is the right test when Spring itself is irrelevant.
A slice test starts only the part of Spring needed for one layer. For example, @WebMvcTest loads the MVC infrastructure and controller layer, while @DataJpaTest loads the JPA repository layer.
A full integration test uses @SpringBootTest and loads the complete Boot application context. That is useful, but expensive.
Exam tip: Do not default to
@SpringBootTestfor every test. The exam often asks for the most focused test annotation. If the scenario only tests a controller,@WebMvcTestis usually the better answer.
@SpringBootTest - Full Application Context
@SpringBootTest tells Spring Boot to find your main application configuration, start an ApplicationContext through SpringApplication, and apply Boot features such as auto-configuration, externalized configuration, and component scanning.
@SpringBootTest
class OrderServiceIntegrationTest {
@Autowired
private OrderService orderService;
@Test
void placesOrder() {
Receipt receipt = orderService.placeOrder(new OrderRequest("sku-123"));
assertThat(receipt.status()).isEqualTo(OrderStatus.ACCEPTED);
}
}This test can inject controllers, services, repositories, configuration properties, event publishers, and any other beans that are part of the application context.
That is the key strength and the key cost: the full context makes the test realistic, but it also makes it slower and more fragile if unrelated application wiring breaks.
Exam tip:
@SpringBootTestloads the full application context. It is not a web-layer slice, data-layer slice, or unit-test annotation.
How Boot finds the application configuration
When you use @SpringBootTest without explicitly naming configuration classes, Spring Boot searches upward from the test package until it finds a class annotated with @SpringBootConfiguration. In normal Boot apps, that is the class annotated with @SpringBootApplication.
@SpringBootApplication
public class ExamPrepApplication {
public static void main(String[] args) {
SpringApplication.run(ExamPrepApplication.class, args);
}
}If your test is outside the package tree of the main application class, Boot may fail to find configuration. In that case, specify it:
@SpringBootTest(classes = ExamPrepApplication.class)
class ExternalPackageIntegrationTest {
}Exam tip:
@SpringBootApplicationincludes@SpringBootConfiguration. That is why@SpringBootTestcan usually discover your application class automatically.
@SpringBootTest does not always mean HTTP
The most common trap: @SpringBootTest does not start a real embedded server by default. The default web environment is MOCK, which creates a mock servlet environment when Spring MVC is on the classpath.
@SpringBootTest
class DefaultSpringBootTest {
// Full application context, but no real HTTP server by default
}If the question says the test must make a real HTTP request to a running application, look for webEnvironment = RANDOM_PORT or DEFINED_PORT.
Exam tip:
@SpringBootTestdefaults towebEnvironment = MOCK. It loads a web application context when MVC is present, but it does not start Tomcat on port 8080.
Web Environment Modes
@SpringBootTest has a webEnvironment attribute that controls whether Boot starts a server.
| Mode | Starts Real Server | Servlet Environment | Use When |
|---|---|---|---|
MOCK | No | Mock servlet environment | Full context, MVC tested through MockMvc |
RANDOM_PORT | Yes | Real embedded server | End-to-end HTTP test on a random port |
DEFINED_PORT | Yes | Real embedded server | End-to-end HTTP test on configured port |
NONE | No | No web environment | Non-web application tests |
MOCK is the default:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class MockWebEnvironmentTest {
}Use RANDOM_PORT when the test needs a real HTTP server:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RealHttpIntegrationTest {
@LocalServerPort
private int port;
@Test
void applicationStartsOnRandomPort() {
assertThat(port).isPositive();
}
}RANDOM_PORT avoids port conflicts on CI because each test run gets an available port.
DEFINED_PORT uses the configured server port, usually from server.port. It is rarely the best choice for automated tests because it can collide with a running local app or another test process.
NONE creates a non-web application context:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
class BatchJobIntegrationTest {
}Exam tip: If a question asks for a real embedded server,
RANDOM_PORTis usually the safest answer. If it only asks to test MVC controller behavior,MockMvcis often enough.
MockMvc and @AutoConfigureMockMvc
MockMvc lets you test Spring MVC request handling without starting a real servlet container. It sends requests into the MVC infrastructure and lets you assert status codes, headers, views, JSON, and response bodies.
With a full application context, add @AutoConfigureMockMvc:
@SpringBootTest
@AutoConfigureMockMvc
class OrderControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
void returnsOrder() throws Exception {
mockMvc.perform(get("/orders/42"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(42));
}
}This combination loads the full context and configures MockMvc against it. Services, repositories, filters, converters, exception handlers, and application configuration are all part of the test unless you replace them.
MockMvc is not the same as a real HTTP client. It does not open a socket, does not bind to a real port, and does not test servlet-container behavior below the MVC layer.
Exam tip:
@SpringBootTestalone does not injectMockMvc. Add@AutoConfigureMockMvc, or use@WebMvcTest, which auto-configuresMockMvcby default.
What MockMvc is good at
Use MockMvc when you need to assert MVC behavior:
mockMvc.perform(post("/orders")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{"sku": "spring-book", "quantity": 1}
"""))
.andExpect(status().isCreated())
.andExpect(header().string("Location", "/orders/42"));It is especially useful for:
- HTTP status codes.
- Request mapping.
- Validation errors.
- JSON serialization.
@ControllerAdviceexception handling.- Security filter behavior when Spring Security test support is configured.
Exam tip:
MockMvctests the Spring MVC layer in a mock servlet environment. It is not a browser test and not a real network test.
@WebMvcTest - MVC Slice Testing
@WebMvcTest is the focused annotation for Spring MVC controller tests. It disables full auto-configuration and applies only configuration relevant to MVC tests.
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private OrderService orderService;
@Test
void returnsOrder() throws Exception {
given(orderService.findById(42L))
.willReturn(new OrderDto(42L, "spring-book"));
mockMvc.perform(get("/orders/42"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.sku").value("spring-book"));
}
}This test loads OrderController and MVC infrastructure, not the whole application.
The controller's dependency, OrderService, is supplied with @MockBean because regular @Service beans are not loaded in a web slice.
Exam tip:
@WebMvcTestis for controller-layer tests. It does not load@Serviceor@Repositorybeans unless you explicitly import or mock what the controller needs.
What @WebMvcTest loads
The exact list changes slightly across Boot versions, but the exam-level rule is stable: @WebMvcTest loads MVC components, not the whole application.
| Bean Type | Loaded by @WebMvcTest? | Why |
|---|---|---|
@Controller / @RestController | Yes | The web layer under test |
@ControllerAdvice | Yes | Exception handling and binding advice |
Filter | Yes | Servlet filter behavior relevant to MVC |
HandlerInterceptor | Yes | MVC request interception |
WebMvcConfigurer | Yes | MVC customization |
Converter / GenericConverter | Yes | Request and response conversion |
@Service | No | Business layer is outside the slice |
@Repository | No | Data layer is outside the slice |
Regular @Component | No | Not part of the default MVC slice |
@ConfigurationProperties | No by default | Enable explicitly if needed |
If the controller needs a collaborator, mock it:
@MockBean
private PaymentService paymentService;Or import a small configuration class when the test genuinely needs a real MVC helper:
@WebMvcTest(OrderController.class)
@Import(JsonConfig.class)
class OrderControllerJsonTest {
}Exam tip: If a
@WebMvcTestfails becauseOrderServiceis missing, the fix is usually@MockBean OrderService, not switching blindly to@SpringBootTest.
@WebMvcTest and Spring Security
When Spring Security is on the classpath, web MVC tests may include security filters. That means a request that looks correct can return 401 Unauthorized or 403 Forbidden unless the test supplies an authenticated user or disables filters intentionally.
@WebMvcTest(AdminController.class)
class AdminControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser(roles = "ADMIN")
void allowsAdminAccess() throws Exception {
mockMvc.perform(get("/admin/reports"))
.andExpect(status().isOk());
}
}Security details are covered in the Spring Security section of the exam, but the testing angle is simple: controller slice tests can still pass through configured filters.
Exam tip:
@WebMvcTestauto-configuresMockMvc. Do not add@AutoConfigureMockMvcjust to getMockMvcin a standard web slice test.
@SpringBootTest vs @WebMvcTest
This comparison is one of the highest-value testing topics for the exam.
| Question | @SpringBootTest | @WebMvcTest |
|---|---|---|
| Loads full application context? | Yes | No |
| Focuses only on MVC layer? | No | Yes |
Loads @Service beans? | Yes | No |
Loads @Repository beans? | Yes | No |
Auto-configures MockMvc? | No, add @AutoConfigureMockMvc | Yes |
| Usually faster? | No | Yes |
| Best for | Integration wiring | Controller behavior |
| Typical mock style | Optional replacement of real beans | Required for controller collaborators |
Use @SpringBootTest when the point is full application integration:
@SpringBootTest
class CheckoutFlowTest {
// tests service + repository + configuration wiring together
}Use @WebMvcTest when the point is controller behavior:
@WebMvcTest(CheckoutController.class)
class CheckoutControllerTest {
// tests request mapping, validation, JSON, and status codes
}Do not combine them:
@SpringBootTest
@WebMvcTest(OrderController.class) // Wrong: conflicting test strategies
class ConfusedTest {
}That asks Boot to load the full application context and a sliced MVC context at the same time. Pick the test strategy that matches the scenario.
Exam tip:
@SpringBootTestand@WebMvcTestare alternatives, not complementary annotations. If you see both on the same test class, treat it as a configuration mistake.
@DataJpaTest - Repository Slice Testing
@DataJpaTest is the focused slice annotation for JPA repository tests. It configures the JPA infrastructure, scans repositories and entities, and runs tests transactionally.
@DataJpaTest
class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
void findsByEmail() {
userRepository.save(new User("a@example.com"));
Optional<User> result = userRepository.findByEmail("a@example.com");
assertThat(result).isPresent();
}
}This is the right test when you want to verify:
- Derived query method names.
@Querydefinitions.- Entity mappings.
- Repository behavior.
- Persistence constraints.
It is not the right test for controller behavior or service orchestration.
Exam tip:
@DataJpaTestis a JPA repository slice. It does not load controllers, MVC infrastructure, or your full service layer.
Test database behavior
By default, Boot tries to configure an embedded database for @DataJpaTest if one is available. This keeps repository tests isolated from your production database.
If you want to use a configured test database instead, Boot provides test database replacement controls such as @AutoConfigureTestDatabase.
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class RepositoryWithRealTestDatabaseTest {
}Use that only when the test database is intentionally configured for the test environment.
Exam tip:
@DataJpaTestis usually transactional and rolls back after each test method. That is why repository tests can insert data without manually cleaning the database in the common case.
Other Test Slices You Should Recognize
The exam focuses most heavily on @SpringBootTest, @WebMvcTest, and @DataJpaTest, but you should recognize the broader slice-test pattern.
| Annotation | Focus | Common Use |
|---|---|---|
@WebMvcTest | Spring MVC | Controller tests with MockMvc |
@DataJpaTest | JPA | Repository and entity persistence tests |
@JsonTest | JSON | Jackson serialization and deserialization |
@JdbcTest | JDBC | JDBC-based data access without JPA |
@RestClientTest | REST clients | Testing outbound REST client code |
The shared idea: each slice loads only the auto-configuration and beans needed for one layer.
That keeps tests faster and reduces noise. If a controller test fails, you do not want a broken database migration to be the reason. If a repository test fails, you do not want a controller validation rule to be involved.
Exam tip: A slice test is not a smaller
@SpringBootTest. It deliberately limits the context to one layer and excludes unrelated beans.
@MockBean vs @Mock vs @SpyBean
Spring Boot testing uses Mockito, but not every Mockito annotation affects the Spring context.
| Annotation | Creates Spring Bean? | Use When |
|---|---|---|
@Mock | No | Plain unit tests outside Spring |
@MockBean | Yes | Replace or add a bean in the Spring test context |
@SpyBean | Yes | Wrap an existing Spring bean and partially mock or verify it |
@Mock
@Mock creates a Mockito mock in the test class. Spring does not know about it.
@ExtendWith(MockitoExtension.class)
class OrderServiceUnitTest {
@Mock
private PaymentClient paymentClient;
@InjectMocks
private OrderService orderService;
}This is correct for a plain unit test.
@MockBean
@MockBean registers a Mockito mock as a bean in the Spring application context. If a bean of the same type already exists, it replaces it.
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@MockBean
private OrderService orderService;
}The controller can now receive the mock through dependency injection.
Exam tip: In a Spring test,
@MockBeanis visible to the container. A plain@Mockis not injected into Spring-managed beans unless you wire it yourself.
@SpyBean
@SpyBean wraps a real Spring bean with a Mockito spy. The real methods run unless you stub them.
@SpringBootTest
class NotificationIntegrationTest {
@SpyBean
private EmailSender emailSender;
@Test
void sendsConfirmationEmail() {
// run flow
verify(emailSender).sendConfirmation(any());
}
}Use spies sparingly. They are useful for verifying integration behavior, but they can make tests tightly coupled to implementation details.
Version note: Newer Spring Boot versions have introduced newer Mockito bean annotations in some documentation. For the Spring Boot 2.x exam context,
@MockBeanand@SpyBeanare the names you should recognize.
Test Profiles and Test Properties
Spring tests can activate profiles and override properties just like a running application.
Use @ActiveProfiles to activate a profile:
@SpringBootTest
@ActiveProfiles("test")
class PaymentServiceTest {
}That loads profile-specific configuration such as:
# src/test/resources/application-test.properties
payment.gateway.enabled=falseUse inline properties for a one-off test:
@SpringBootTest(properties = "app.payment.enabled=false")
class PaymentDisabledTest {
}Use @TestPropertySource when you want a dedicated test property file:
@SpringBootTest
@TestPropertySource(locations = "classpath:/payment-test.properties")
class PaymentPropertySourceTest {
}The precedence rules can get detailed, but the exam-level concept is straightforward: test properties and active profiles let the test context differ from production configuration.
Exam tip: Profiles select configuration. They do not automatically mock dependencies, replace repositories, or disable external calls unless the profile's beans/properties actually do that.
Testing profile-specific beans
Profiles can control which beans exist:
@Configuration
public class PaymentConfig {
@Bean
@Profile("prod")
PaymentClient realPaymentClient() {
return new StripePaymentClient();
}
@Bean
@Profile("test")
PaymentClient fakePaymentClient() {
return new FakePaymentClient();
}
}With @ActiveProfiles("test"), the test receives FakePaymentClient.
@SpringBootTest
@ActiveProfiles("test")
class CheckoutTest {
@Autowired
private PaymentClient paymentClient;
}This is different from @MockBean: the fake bean is a real class selected by profile, not a Mockito mock.
Transaction Rollback in Tests
Spring Test supports transactional test methods. When a test method runs inside a test-managed transaction, Spring rolls it back by default after the test completes.
That is why this repository test can write data without cleanup:
@DataJpaTest
class ProductRepositoryTest {
@Autowired
private ProductRepository productRepository;
@Test
void savesProduct() {
productRepository.save(new Product("spring-guide"));
assertThat(productRepository.count()).isEqualTo(1);
}
}After the method, the transaction rolls back.
You can override rollback with @Commit:
@DataJpaTest
class ProductRepositoryCommitTest {
@Test
@Commit
void savesProductPermanently() {
// committed instead of rolled back
}
}Or with @Rollback(false):
@DataJpaTest
class ProductRepositoryNoRollbackTest {
@Test
@Rollback(false)
void savesProductPermanently() {
// committed instead of rolled back
}
}Be careful with full HTTP tests:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RealHttpOrderTest {
}The test method and server-side request handling may not share the same transaction boundary. Do not assume that annotating the test method with @Transactional automatically rolls back all database work done by the application thread handling the HTTP request.
Exam tip:
@DataJpaTestcommonly rolls back after each test. Real HTTP integration tests withRANDOM_PORTare different because server-side work happens through the running application, not inside the same simple test-method transaction.
JUnit 5 and SpringExtension
Modern Spring Boot tests usually use JUnit Jupiter (JUnit 5).
With Boot test annotations such as @SpringBootTest, @WebMvcTest, and @DataJpaTest, you do not normally need to add @ExtendWith(SpringExtension.class) yourself. These annotations already integrate with the Spring test context framework.
@SpringBootTest
class ModernJUnit5SpringBootTest {
}Older JUnit 4 examples use @RunWith(SpringRunner.class):
@RunWith(SpringRunner.class)
@SpringBootTest
public class LegacyJUnit4SpringBootTest {
}You may see both styles in study materials. Know which test framework the code is using before judging whether the annotation is missing.
Exam tip: In JUnit 5, Boot test annotations already include Spring extension support. In JUnit 4, tests need
@RunWith(SpringRunner.class)for Spring integration.
Common Exam Traps
| Trap | Correct Behavior |
|---|---|
"@SpringBootTest starts a real server by default" | Default is MOCK; use RANDOM_PORT or DEFINED_PORT for a real server. |
"@WebMvcTest loads services" | It loads MVC components; mock service dependencies with @MockBean. |
"@SpringBootTest auto-configures MockMvc" | Add @AutoConfigureMockMvc, or use @WebMvcTest. |
"@Mock replaces a Spring bean" | Use @MockBean to register a mock in the Spring context. |
"@DataJpaTest is for service-layer tests" | It is a JPA repository slice. |
"@SpringBootTest and @WebMvcTest should be combined" | They represent different test strategies. Pick one. |
| "Profiles mock dependencies automatically" | Profiles only activate profile-specific configuration. |
| "All transactional tests roll back server-side HTTP work" | Real HTTP tests may run application code in a different thread/transaction. |
If you remember one rule, make it this: the annotation tells you how much of the application context exists.
@WebMvcTest means MVC slice. @DataJpaTest means JPA slice. @SpringBootTest means full application context.
Exam Quick Reference
| Need | Use |
|---|---|
| Full application context | @SpringBootTest |
| Full context without real server | @SpringBootTest default / webEnvironment = MOCK |
| Real HTTP server on random port | @SpringBootTest(webEnvironment = RANDOM_PORT) |
Full context plus MockMvc | @SpringBootTest + @AutoConfigureMockMvc |
| Controller slice | @WebMvcTest |
| Controller dependency mock | @MockBean |
| Repository/JPA slice | @DataJpaTest |
| Plain Mockito unit test | @Mock + @InjectMocks |
| Replace Spring bean with Mockito mock | @MockBean |
| Spy on a real Spring bean | @SpyBean |
| Activate test profile | @ActiveProfiles("test") |
| Override one property | @SpringBootTest(properties = "...") |
| Dedicated test property source | @TestPropertySource |
| Commit transactional test data | @Commit or @Rollback(false) |
Frequently Asked Questions
What is the difference between @SpringBootTest and @WebMvcTest?
@SpringBootTest loads the full Spring Boot application context. It is used for integration tests where you need real application wiring across multiple layers.
@WebMvcTest loads only the Spring MVC slice. It is used for controller tests with MockMvc, and service or repository dependencies must usually be provided with @MockBean.
Does @SpringBootTest start a real server?
Not by default. The default webEnvironment is MOCK, which creates a mock servlet environment but does not start an embedded server. Use @SpringBootTest(webEnvironment = RANDOM_PORT) or DEFINED_PORT to start a real server.
Does @WebMvcTest load service beans?
No. @WebMvcTest focuses on MVC components such as controllers, controller advice, filters, converters, and MVC configuration. If the controller depends on a service, provide it with @MockBean or import a specific test configuration.
When should I use @DataJpaTest?
Use @DataJpaTest when testing JPA repositories, entity mappings, derived queries, @Query methods, and persistence constraints. It loads the JPA slice, not the full application context.
What is the difference between @Mock and @MockBean?
@Mock creates a Mockito mock in the test class only. Spring does not know about it. @MockBean creates or replaces a bean in the Spring test context, so Spring-managed objects can receive it through dependency injection.
Is MockMvc a real HTTP server?
No. MockMvc drives Spring MVC in a mock servlet environment. It is excellent for controller behavior, request mapping, JSON, validation, and status-code assertions, but it does not open a real network port.
Next Steps
Testing questions are easiest to miss when you recognize the annotation but forget what it loads. Practice with scenario-based questions until you can identify the test type from the setup alone.
Start with the free Spring Professional practice questions, then review the broader 8-week study plan. For adjacent topics, read the Spring Data JPA exam guide for repository behavior and the Spring Boot auto-configuration guide for how Boot decides which test infrastructure to create.