Put Request in Spring - Testing

Test POST Request in Spring Java Application

In this article we will show you how to perform a POST request in Spring using RestTemplate. We will take you through a simple integration test example to be able to test a POST endpoint in a Spring Boot application.

Let’s start!

Setup

For the examples shown throughout this article, we will be using the same codebase we’ve shown in our previous Spring articles. You can see the latest version in our article “Exception Handling in Spring Rest Services in Java”.

In this article will look at our POST endpoint only, and write an integration test for it. Our POST endpoint looks like this:

    @PostMapping(consumes = { MediaType.APPLICATION_JSON_VALUE })
    ResponseEntity<String> createCustomer(@RequestBody Customer customer, UriComponentsBuilder uriBuilder) {
        return repository.create(customer)
                .onSuccessDo(e -> ResponseEntity
                        .created(uriBuilder
                                .path("/api/customers/{id})")
                                .buildAndExpand(e.entity().id())
                                .toUri()
                        ).build()).response();
    }

As you can see in this example, we are using a custom Either monad to handle the responses, but for the purpose of this article you won’t have to worry about implementation details. Integration testing is a kind of black box test, we only care about the response, so the internals of the service should be completely opaque for the purposes of testing.

One more thing you’ll need in your application to be able to test it, is the Spring Boot test dependencies. You can import them by importing the Spring Boot starter test package in your build.gradle file:

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	implementation("org.springframework.boot:spring-boot-starter-web")

	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

You will also notice that we are not specifying the version in our dependency import, this is because we have previously defined the Spring Boot version in our plugins section:

plugins {
	id 'java'
	id 'org.springframework.boot' version '2.7.6'
	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}

Now that we have the very basics of our Spring Boot application, we can now start with out test. If for any reason you need help to set up your initial application, you can check our article “How to Bootstrap a Spring Boot Application”.

Let’s start looking at our integration test then!

Test Post Request in Spring

There are different ways to test a POST request in Spring, depending on what your needs are. We will start by looking at postForEntity method, let’s see how it works.

Using PostForEntity method

The postForEntity method allows us to easily test a POST endpoint in our application by sending a simple POST request in Spring, although it has some limitations. Let’s see how it works:

package com.theboreddev.springbootjava;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CustomerControllerIntegrationTest {

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate template;

    @Test
    public void shouldReturn201ResponseIncludingLocationHeaderAndCreateCustomerInOurDatabase() {

        final Customer newCustomer = new Customer(null, "Rowan Atkinson", 52);

        final ResponseEntity<String> response = template.postForEntity(String.format("http://localhost:%d/api/customers", port), newCustomer, String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
        assertThat(response.getBody()).isNull();
        assertThat(response.getHeaders().getLocation().toString()).isEqualTo(String.format("http://localhost:%d/api/customers/0", port));
    }
}

You can see how we use postForEntity method to send a new customer to the url POST /api/customers. Once we receive the response, we check that the status code is 201 (CREATED), the body is empty (null) and the location header we receive is correct.

We mentioned before that postForEntity has some limitations, one of them is the inability to include headers in our request. In cases where we need to pass headers in our integration test, we can use exchange method present in TestRestTemplate.

At the moment we support JSON format only, what if we wanted to support a different media type like XML? How would our POST request in Spring look like?

POST Request in Spring - JSON
Photo by Ferenc Almasi on Unsplash

Using Exchange Method

To be able to understand how to use this method, we are going to change our example slightly. We are going to modify our POST endpoint so it can consume either JSON or XML.

To achieve this, we’ll just have to add XML to the consumes argument in our PostMapping annotation:

@PostMapping(consumes = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })

This means that, in order to test both JSON and XML responses, we’ll need to send a Content-Type header in our test. Our existing test won’t need to change, as the default format will be JSON if we don’t specify a Content-Type header in our test. Let’s see how can we send XML then!

    @Test
    public void shouldReturn201ResponseIncludingLocationHeaderAndCreateCustomerSentInXMLFormat() {

        final HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", MediaType.APPLICATION_XML_VALUE);
        final Customer newCustomer = new Customer(null, "Rowan Atkinson", 52);

        final ResponseEntity<Customer> response = template.exchange(
                String.format("http://localhost:%d/api/customers", port),
                HttpMethod.POST,
                new HttpEntity<>(newCustomer, headers),
                Customer.class
        );

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
        assertThat(response.getBody()).isNull();
        assertThat(response.getHeaders().getLocation().toString()).isEqualTo(String.format("http://localhost:%d/api/customers/0", port));
    }

You can notice how now we’re using exchange method in our test. To make it work it was very simple, we just need to send Content-Type: application/xml and add the media type to our controller, as we saw earlier.

This is very straightforward, isn’t it?

Conclusion

in this article we’ve learned the two ways we have to write a simple integration test for a POST request in Spring Boot. We’ve also seen how to support two different content types in our endpoints in a very easy manner.

This is all from us today! We hope you’ve found this article useful and easy to read.

Looking forward to seeing you with us very soon again! Please follow us if you want to subscribe to more of our content!

Thanks for reading us!