Put Request in Spring - Testing

Test PUT Request in Spring Java Application

In this article we will show you how to perform a PUT request in Spring using RestTemplate. We will take you through a simple integration test example to be able to test a PUT 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 PUT endpoint only, and write an integration test for it. Our PUT endpoint looks like this:

    @PutMapping(value = "/{id}", consumes = { MediaType.APPLICATION_JSON_VALUE })
    ResponseEntity<String> updateCustomer(@PathVariable("id") Long id, @RequestBody Customer customer) {
        return repository.update(id, customer)
                .onSuccessDo(e -> ResponseEntity.noContent().build())
                .onFailureDo(e -> {
                    if (e.exception() instanceof CustomerNotFound) {
                        return ResponseEntity.notFound().build();
                    } else {
                        return ResponseEntity.internalServerError().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 PUT Request in Spring

TestRestTemplate does not have an exact equivalent to getForEntity or postForEntity methods we saw in our previous articles. There is a put method, although it’s difficult to test with it because it’s a void method. This means that asserting the response cannot be done in our integration test.

Therefore, we are going to use exchange method in our integration test. Let’s see how it works!

Using Exchange method

The exchange method allows us to easily test a PUT endpoint in our application by sending a simple PUT request in Spring.

First of all, we will create a test to make sure that updating a non-existing customer returns 404 (NOT_FOUND).

    @Test
    public void shouldReturn404WhenTryingToUpdateANonExistingCustomer() {

        final Customer updated = new Customer(99L, "Rowan Atkinson", 53);

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

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
    }

Because we haven’t persisted any customer yet, we won’t be able to update a customer with the given ID and we get back a 404 response, as we anticipated.

Let’s test now our successful scenario, we are going to create a customer using POST first and then update it using PUT.

    @Test
    public void shouldReturn204ResponseAndUpdateCustomer() {

        final Customer newCustomer = new Customer(99L, "Margaret McGuinness", 27);
        template.postForEntity(String.format("http://localhost:%d/api/customers", port), newCustomer, String.class);
        final Customer updated = new Customer(99L, "Rowan Atkinson", 53);

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

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
        assertThat(response.getBody()).isNull();
    }

In this case, if the customer was successfully updated, we receive a 204 (NO_CONTENT) response with empty (or null) body. As simple as this!

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

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

Supporting headers

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 PutMapping annotation:

@PutMapping(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 shouldReturn204ResponseAndUpdateCustomerinXmlFormat() {

        final Customer newCustomer = new Customer(99L, "Margaret McGuinness", 27);
        template.postForEntity(String.format("http://localhost:%d/api/customers", port), newCustomer, String.class);

        final HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", MediaType.APPLICATION_XML_VALUE);
        final Customer updated = new Customer(99L, "Rowan Atkinson", 53);

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

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
        assertThat(response.getBody()).isNull();
    }

You can notice how we’re now passing a headers object to our exchange method in our test. To make it work it was very simple, we just needed 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 how to write a simple integration test for a PUT 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!