Course outline

Spring Boot / Intermediate: data, security, and framework evidence / Lesson 10

Testing slices and evidence boundaries

Choose domain, MVC, configuration, and persistence tests according to the behavior they can actually observe, using controlled identities and dependencies.

About 5 min readContent reviewed
On this page

Lesson goals

  • Distinguish direct calls, slices, and application contexts.
  • Read MockMvc and MockitoBean fixtures in the pinned baseline.
  • Avoid listener and transaction-cleanup assumptions.

Select the narrowest test that sees the risk

A domain unit test can observe calculation and explicit collaborators. A web slice can observe routing, conversion, advice, and selected filter integration. A configuration context can observe binding and bean conditions. A database integration test can observe actual mapping and transaction behavior. Each answers a different question; a large test suite can still miss the one boundary a claim depends on.

Use requirement-derived assertions instead of mirroring private field names or every implementation branch. Test invalid, absent, forbidden, and conflict states as well as success. Keep fixtures synthetic and bound in size. A test name should reveal the behavior and the evidence boundary, such as โ€œmissing product returns404 through MVC,โ€ rather than merely โ€œtest service.โ€

Exercise an intended web contract without a real port

The illustrative @WebMvcTest loads the selected controller slice and explicitly imports the intended security chain. @MockitoBean, available in the matching Framework6.2 test support, provides the contextual lookup capability. A synthetic authority permits the GET under the shown policy. Appropriate Boot test and security-test dependencies are required; the fixture is not run here and no listener is started.

MockMvc dispatches through a mock servlet environment rather than proving a production network path. It can test selected filters and MVC serialization, while a mock principal does not verify real credentials. Keep expected fields exact and include missing/forbidden cases. Do not disable all filters merely to get a200 assertion unless the test explicitly targets a different lower-level boundary.

java Illustrative Boot3.5 MVC/security slice
package training.catalog;

import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(ProductController.class)
@Import(ApiSecurity.class)
class ProductControllerTest {
    @Autowired MockMvc mvc;
    @MockitoBean ProductService products;
    @Test
    @WithMockUser(authorities="catalog:read")
    void knownProduct() throws Exception {
        given(products.find(42L)).willReturn(Optional.of(new ProductView(42L,"Notebook")));
        mvc.perform(get("/products/42"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.id").value(42))
            .andExpect(jsonPath("$.name").value("Notebook"));
    }
}

This test requires the contextual controller/service contracts and managed test dependencies. Its assertions are intended, not an observed framework test run.

Keep configuration tests limited to their intended beans

ApplicationContextRunner can configure a small context and assert bean presence, property binding, or expected startup failure. The second fixture imports only the properties configuration and supplies bounded values. A real run would need the validation/binding dependencies. It is not an instruction to load the entire application's production properties or connect to configured integrations. Syntax review alone is narrower than executing this context.

Also test an absent required value, an invalid page bound, profile conditions, and conflicting overrides. A broad @SpringBootTest can accidentally initialize schedulers, clients, or data access that the question did not require. Choose and restrict the context deliberately. A test that passes because it never registered the relevant configuration is not evidence that the configuration is correct.

java Illustrative limited properties-binding test
package training.catalog;

import java.time.Duration;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;

class CatalogConfigurationTest {
    private final ApplicationContextRunner runner = new ApplicationContextRunner()
        .withUserConfiguration(CatalogConfiguration.class);
    @Test
    void bindsKnownProperties() {
        runner.withPropertyValues("catalog.max-page-size=20","catalog.timeout=2s")
            .run(context -> {
                assertThat(context).hasNotFailed();
                assertThat(context).hasSingleBean(CatalogProperties.class);
                assertThat(context.getBean(CatalogProperties.class).maxPageSize()).isEqualTo(20);
                assertThat(context.getBean(CatalogProperties.class).timeout()).isEqualTo(Duration.ofSeconds(2));
            });
    }
}

The fixture states intended assertions for the selected configuration. No context refresh or validator execution is claimed.

Know when a test starts infrastructure

@SpringBootTest defaults to a mock web mode rather than a real listening server; RANDOM_PORT and DEFINED_PORT start embedded listeners. NONE uses a non-web context but can still initialize user beans and external integrations. Select the mode and dependencies explicitly. This course starts none of those contexts or listeners merely to claim a framework execution result.

A transaction around a test method does not automatically roll back work performed by an HTTP server on another thread. Likewise, a test using another database dialect may not reproduce production locks or migrations. Use explicit cleanup and isolated approved storage when such integration is authorized. Never rely on โ€œtestโ€ in an annotation to protect a configured project datasource.

Make failures and evidence reproducible

Control clocks, identifiers, fakes, and asynchronous scheduling rather than sleeping until an assertion usually passes. Observe failed futures and verify cleanup. Assert no forbidden mutation after rejection, not only an error status. Keep contract fixtures and consumer checks for public DTO evolution, while avoiding brittle snapshots of internal exception lines or unstable diagnostic text.

Record the test scope, dependencies, runtime, command, and actual outcome. Plain Java models and JSON/YAML/XML parsing in this course are labeled separately from unexecuted Spring tests. A helpful suite tells the next maintainer which behavior regressed and which boundary was observed. Passing code discovery or compiling a DTO is not equivalent to passing MVC, security, and persistence integration.

Common errors and fixes

A test unexpectedly opened a listening port

Why it happens
A real web-environment mode was selected.
How to fix it
Use an appropriate mock/non-web boundary or explicitly authorized isolated integration mode.
Prevent it next time
Review test startup scope.

A test rollback left server-created data behind

Why it happens
Server work ran on another thread/transaction.
How to fix it
Use isolated storage and explicit cleanup for that test architecture.
Prevent it next time
Do not infer rollback from the test method alone.

A web test passed only after all filters were disabled

Why it happens
The test stopped observing the security boundary it claimed to cover.
How to fix it
Retain intended filters and supply a controlled valid identity or CSRF case.
Prevent it next time
Name the evidence boundary explicitly.

Check your understanding

1. Which test best observes JSON binding and the selected400 problem mapping without a real port?

Show worked solution

A properly configured MVC slice or mock-web fixture including the controller, relevant advice, validator, and selected filters. A direct method call skips those integrations, while a full random-port environment adds an unrelated listener and different transaction considerations.

2. Why should a MockitoBean result not be cited as evidence of generated SQL correctness?

Show worked solution

The mock substitutes application behavior and executes no JPA query. It can prove delegation and transport mapping for the supplied result, while mappings, constraints, query plans, and transaction semantics need their own controlled database tests.

Key takeaways

  • Match the test scope to the behavior being claimed.
  • Mock web, configuration contexts, and real listeners are different modes.
  • Truthful evidence includes failure paths, cleanup, and explicit limitations.

Official references