A TestRestTemplate test sends a real HTTP request to the server started by SpringBootTest. For a POST endpoint, it can check the status, response body and Location header together. It also returns 4xx responses for your assertions instead of raising the client exception that a regular RestTemplate normally raises.
This tutorial uses Spring Boot 4.1.2 and Java 17 or later. A reservation API accepts a product code and quantity, assigns an ID, and makes the new reservation available at its own URL. The test will submit a reservation, fetch it by that URL, and submit an invalid request to check the error response.
1. Add the Spring Boot 4 Dependencies
The application needs Spring MVC for its endpoint. The test needs the MVC test starter, the REST client classes and the TestRestTemplate module. In Spring Boot 4, TestRestTemplate is in org.springframework.boot.resttestclient and is configured explicitly with AutoConfigureTestRestTemplate. Older examples that import it from org.springframework.boot.test.web.client will not compile against this version.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.2</version>
<relativePath/>
</parent>
<properties><java.version>17</java.version></properties>
<dependencies>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webmvc</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webmvc-test</artifactId><scope>test</scope></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-restclient</artifactId><scope>test</scope></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-resttestclient</artifactId><scope>test</scope></dependency>
</dependencies>
The test dependencies are scoped to test because the production reservation endpoint does not use an HTTP client. Spring Boot’s parent manages their versions. The complete Maven project is linked at the end.
2. Create a Reservation Endpoint
A valid POST to /reservations creates a reservation and returns HTTP 201 Created. The response body contains the assigned ID; the Location header points to /reservations/{id}. The GET endpoint lets the test check that the reservation was stored, rather than merely echoed in the POST response.
@RestController
public class ReservationController {
private final AtomicLong nextId = new AtomicLong(100);
private final Map<Long, Reservation> reservations = new ConcurrentHashMap<>();
public record CreateReservation(String productCode, int quantity) {}
public record Reservation(long id, String productCode, int quantity) {}
public record ErrorResponse(String message) {}
@PostMapping("/reservations")
ResponseEntity<?> create(@RequestBody CreateReservation request) {
if (request.productCode() == null || request.productCode().isBlank()
|| request.quantity() <= 0) {
return ResponseEntity.badRequest()
.body(new ErrorResponse("Product code and positive quantity are required"));
}
long id = nextId.getAndIncrement();
Reservation reservation = new Reservation(id, request.productCode(), request.quantity());
reservations.put(id, reservation);
return ResponseEntity.created(URI.create("/reservations/" + id)).body(reservation);
}
@GetMapping("/reservations/{id}")
ResponseEntity<Reservation> get(@PathVariable long id) {
Reservation reservation = reservations.get(id);
return reservation == null ? ResponseEntity.notFound().build() : ResponseEntity.ok(reservation);
}
}
The map keeps this example small. In an application, a repository would usually store the reservation. The HTTP contract remains the same: success returns an ID and a resource URL, while a blank product code or nonpositive quantity returns a 400 response with an error message.
3. Start a Real Server for the Test
SpringBootTest.WebEnvironment.RANDOM_PORT starts the embedded server on an available port. AutoConfigureTestRestTemplate then supplies a client that resolves a relative URL such as /reservations to that server. The test does not have to discover or hardcode the chosen port.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureTestRestTemplate
class ReservationControllerTest {
@Autowired TestRestTemplate client;
// Tests appear in the following sections.
}
Import TestRestTemplate from org.springframework.boot.resttestclient and AutoConfigureTestRestTemplate from org.springframework.boot.resttestclient.autoconfigure. These are the Spring Boot 4 package names. Because the client crosses an actual HTTP connection, this is an integration test, even though both the client and server run in one test process.
4. POST a Reservation and Read It Back
postForEntity() accepts the relative URL, a Java request record and the expected response type. Spring serializes the record to JSON and maps the response JSON to Reservation. The returned ResponseEntity also exposes the status and headers.
@Test
void createsReservationAndCanReadItBack() {
var request = new ReservationController.CreateReservation("LAPTOP-15", 2);
ResponseEntity<ReservationController.Reservation> created = client.postForEntity(
"/reservations", request, ReservationController.Reservation.class);
assertEquals(HttpStatus.CREATED, created.getStatusCode());
assertNotNull(created.getBody());
assertEquals("LAPTOP-15", created.getBody().productCode());
assertEquals(2, created.getBody().quantity());
assertNotNull(created.getHeaders().getLocation());
assertEquals("/reservations/" + created.getBody().id(),
created.getHeaders().getLocation().toString());
ResponseEntity<ReservationController.Reservation> fetched = client.getForEntity(
created.getHeaders().getLocation(), ReservationController.Reservation.class);
assertEquals(HttpStatus.OK, fetched.getStatusCode());
assertEquals(created.getBody(), fetched.getBody());
}
The POST assertions check the creation contract. The following GET is a separate HTTP request to the returned location. Its equality assertion shows that the reservation can be retrieved after the POST. The test does not depend on a fixed ID because another test or later implementation may assign a different one.
5. Assert a Rejected POST Response
For a blank product code and zero quantity, the controller returns 400 Bad Request. TestRestTemplate is fault tolerant: it gives the test a ResponseEntity for that 4xx response rather than throwing HttpClientErrorException. This lets the test inspect the error status and JSON body directly.
@Test
void invalidRequestReturns400InsteadOfThrowing() {
var request = new ReservationController.CreateReservation("", 0);
ResponseEntity<ReservationController.ErrorResponse> response = client.postForEntity(
"/reservations", request, ReservationController.ErrorResponse.class);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertEquals("Product code and positive quantity are required",
response.getBody().message());
}
This assertion would fail if the endpoint began accepting invalid reservations or stopped returning its documented error message. It also verifies the default client behavior against an actual server response. For production client code, use a normal RestTemplate JSON POST example instead; its default error handling is different.
6. Choose the Return Value the Test Needs
postForObject() returns only the mapped response body. It works when the test has no reason to inspect the status or headers:
var request = new ReservationController.CreateReservation("MOUSE-7", 1);
ReservationController.Reservation reservation = client.postForObject(
"/reservations", request, ReservationController.Reservation.class);
assertEquals("MOUSE-7", reservation.productCode());
For a creation contract, postForEntity() is generally more useful because the 201 status and Location header are part of the behavior under test. The shorter method does not make those parts available.
| Test question | Useful tool | What it verifies |
|---|---|---|
| Did a real server accept the POST and expose the created resource? | TestRestTemplate.postForEntity() with SpringBootTest | HTTP status, headers, body and follow-up GET |
| Is only the returned body relevant? | TestRestTemplate.postForObject() | Mapped body |
| Does controller behavior work without starting a server? | MockMvc with a controller test | MVC request handling in a mock environment |
The last option serves a different testing boundary. A MockMvc test is useful for focused controller behavior; this tutorial’s client also covers the embedded server and HTTP serialization.
7. Frequently Asked Questions
7.1. Why do I need AutoConfigureTestRestTemplate in Spring Boot 4?
Spring Boot 4 provides the test client through a dedicated module. The annotation requests its auto-configuration for the running-server test. With the REST client and spring-boot-resttestclient dependencies on the test classpath, the client can be injected and can resolve relative URLs to the random port.
7.2. Does TestRestTemplate replace a production RestTemplate?
No. TestRestTemplate is intended for integration tests and is deliberately separate from RestTemplate. Its fault-tolerant handling of HTTP error responses is useful in assertions, but it should not be assumed for application client code.
7.3. What should a new test use if it does not need TestRestTemplate?
The current Spring Boot testing reference also documents RestTestClient for tests against a running server. Choose based on the test API already used by the project. This article focuses on maintaining and writing TestRestTemplate POST tests with the current Boot 4 setup.
8. Conclusion
Configure TestRestTemplate for a random-port SpringBootTest, call postForEntity(), and assert the status, body and Location together. A GET to that location confirms the created resource is retrievable. For an invalid request, assert the returned 400 response directly. All three paths in this example pass as HTTP integration tests on Spring Boot 4.1.2.
The complete runnable code is in the Spring Boot TestRestTemplate POST project folder.
If the usecase is something like below,
@Data <======== from lombok
class Person {
private String firstName;
private String lastName;
//….
}
@Data <======== from lombok
class Employee{
private int employeeId;
private String deptName;
//…………..
}
Then this code snippet simply set all the fields in Person class as null. Can you please tell me how to resolve this issue?
hi i am getting failed to load application context error from spring when i executed your code. can u pls help