All articles
TestingSpring BootExam Tips

Spring Boot Testing Exam Guide (2V0-72.22)

May 9, 202619 min read

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 MockMvc is 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 TypeSpring ContextSpeedBest For
Plain unit testNoFastestBusiness logic with no framework behavior
Slice testPartialFastOne Spring layer, such as MVC or JPA
@SpringBootTestFullSlowestEnd-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 @SpringBootTest for every test. The exam often asks for the most focused test annotation. If the scenario only tests a controller, @WebMvcTest is 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: @SpringBootTest loads 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: @SpringBootApplication includes @SpringBootConfiguration. That is why @SpringBootTest can 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: @SpringBootTest defaults to webEnvironment = 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.

ModeStarts Real ServerServlet EnvironmentUse When
MOCKNoMock servlet environmentFull context, MVC tested through MockMvc
RANDOM_PORTYesReal embedded serverEnd-to-end HTTP test on a random port
DEFINED_PORTYesReal embedded serverEnd-to-end HTTP test on configured port
NONENoNo web environmentNon-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_PORT is usually the safest answer. If it only asks to test MVC controller behavior, MockMvc is 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: @SpringBootTest alone does not inject MockMvc. Add @AutoConfigureMockMvc, or use @WebMvcTest, which auto-configures MockMvc by 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.
  • @ControllerAdvice exception handling.
  • Security filter behavior when Spring Security test support is configured.

Exam tip: MockMvc tests 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: @WebMvcTest is for controller-layer tests. It does not load @Service or @Repository beans 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 TypeLoaded by @WebMvcTest?Why
@Controller / @RestControllerYesThe web layer under test
@ControllerAdviceYesException handling and binding advice
FilterYesServlet filter behavior relevant to MVC
HandlerInterceptorYesMVC request interception
WebMvcConfigurerYesMVC customization
Converter / GenericConverterYesRequest and response conversion
@ServiceNoBusiness layer is outside the slice
@RepositoryNoData layer is outside the slice
Regular @ComponentNoNot part of the default MVC slice
@ConfigurationPropertiesNo by defaultEnable 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 @WebMvcTest fails because OrderService is 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: @WebMvcTest auto-configures MockMvc. Do not add @AutoConfigureMockMvc just to get MockMvc in 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?YesNo
Focuses only on MVC layer?NoYes
Loads @Service beans?YesNo
Loads @Repository beans?YesNo
Auto-configures MockMvc?No, add @AutoConfigureMockMvcYes
Usually faster?NoYes
Best forIntegration wiringController behavior
Typical mock styleOptional replacement of real beansRequired 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: @SpringBootTest and @WebMvcTest are 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.
  • @Query definitions.
  • Entity mappings.
  • Repository behavior.
  • Persistence constraints.

It is not the right test for controller behavior or service orchestration.

Exam tip: @DataJpaTest is 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: @DataJpaTest is 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.

AnnotationFocusCommon Use
@WebMvcTestSpring MVCController tests with MockMvc
@DataJpaTestJPARepository and entity persistence tests
@JsonTestJSONJackson serialization and deserialization
@JdbcTestJDBCJDBC-based data access without JPA
@RestClientTestREST clientsTesting 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.

AnnotationCreates Spring Bean?Use When
@MockNoPlain unit tests outside Spring
@MockBeanYesReplace or add a bean in the Spring test context
@SpyBeanYesWrap 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, @MockBean is visible to the container. A plain @Mock is 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, @MockBean and @SpyBean are 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=false

Use 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: @DataJpaTest commonly rolls back after each test. Real HTTP integration tests with RANDOM_PORT are 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

TrapCorrect 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

NeedUse
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.

Practice This Topic

Reinforce what you've learned with free practice questions and detailed explanations.