Integration Testing with TestRestTemplate
Integration Testing with TestRestTemplate
Unit tests and slice tests are fast and focused, but they leave a critical question unanswered: does the whole stack work together? Integration tests close that gap. Spring Boot's TestRestTemplate lets you fire real HTTP requests against a fully started application server, traverse every layer — security filters, controllers, services, and the database — and assert on the actual HTTP responses a client would receive.
How Integration Tests Differ from Slice Tests
Slice tests like @WebMvcTest and @DataJpaTest load only a thin slice of the Spring context. They are fast precisely because they skip most of the application. Integration tests take the opposite approach: they boot the complete ApplicationContext, start an embedded Tomcat server on a real port, and let you send HTTP requests as any external client would. The trade-off is clear — setup takes several seconds, so integration tests should cover complete user journeys, not every low-level branch.
@SpringBootTest with a Real Port
To start a real HTTP server in tests, set webEnvironment to RANDOM_PORT (or DEFINED_PORT). Spring Boot then binds to an available port and injects the value into your test class via @LocalServerPort.
@LocalServerPort injects whatever port Spring chose, so you always build URLs against the correct value.
TestRestTemplate vs RestTemplate
TestRestTemplate wraps Spring's standard RestTemplate and adds several test-friendly behaviours. Most importantly, it does not throw exceptions on 4xx/5xx responses — it returns a ResponseEntity with the error status so you can assert on error scenarios cleanly. It also disables redirect following by default, which is what you want when testing redirect logic explicitly.
You get the bean for free from Spring Boot's test autoconfiguration when using RANDOM_PORT or DEFINED_PORT — no manual setup needed.
Testing Create, Read, Update, and Delete
A realistic integration test suite exercises the full CRUD surface of a resource:
Sending Headers and Testing Security
Real applications require authentication headers. Use TestRestTemplate.withBasicAuth() for HTTP Basic, or build a custom HttpEntity with the required headers for Bearer token auth:
@Sql script or a @BeforeEach that calls the repo directly) rather than relying on production data. This keeps tests deterministic and independent of whatever is in the database at runtime.
Database State in Integration Tests
Because the full application context runs, all data changes are real. Each test that mutates the database can pollute the next one unless you reset state. There are three common strategies:
- @Transactional on the test class — rolls back after each test. Works well for non-HTTP tests, but has no effect here because
TestRestTemplatesends requests over a real HTTP socket; the server runs in a different transaction scope. - @Sql to clean up — annotate the test method or class with
@Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, scripts = "cleanup.sql"). Explicit and reliable. - Separate test database with a known state — configure
application-test.propertiesto use an H2 in-memory database and setspring.jpa.hibernate.ddl-auto=create-drop. The schema is recreated fresh for every test run.
Activate this profile in the test class by adding @ActiveProfiles("test") to the annotation list.
Asserting on Response Bodies
Prefer asserting on strongly typed response objects rather than raw JSON strings. When you pass a class to getForEntity, Spring uses Jackson to deserialise the response body automatically. For collections, use ParameterizedTypeReference:
Performance Considerations
Integration tests are inherently slower than unit tests. Follow these practices to keep your test suite manageable:
- Reuse the application context. By default Spring Boot caches the context between test classes with the same configuration. Avoid adding
@MockBeanto integration tests — each unique set of mocked beans creates a separate context, multiplying startup costs. - Keep integration tests in a dedicated source set or package. Run them separately from unit tests so developers get fast feedback during development and full coverage in CI.
- Use Testcontainers for production-like databases. In the next lesson you will see how Testcontainers replaces H2 with a real MySQL or PostgreSQL container, eliminating H2 compatibility surprises without sacrificing isolation.
Summary
TestRestTemplate with @SpringBootTest(webEnvironment = RANDOM_PORT) gives you true end-to-end tests: a full Spring context, a real HTTP server, and the ability to exercise every layer of your application with actual HTTP calls. They are the most faithful simulation of production behaviour available without deploying. Use them for critical user journeys, secure the database state per test, lean on ResponseEntity for error-scenario assertions, and keep the suite lean to preserve build speed.