Testing Spring Boot with TestRestTemplate

TestRestTemplate can be used to send http request in our Spring Boot integration tests. Such tests are usually executed with Spring boot run as a local server in a random port @LocalServerPort. So just need to create the request in integration tests and send it like a clients of your servers. TestRestTemplate have all necessary methods to send the request to server with a convenient way similar to RestTemplate.

Let’s see a basic example. Here is a minimal controller:

@RestController
public class PingController {
  @GetMapping("/hello")
  public String echo() {
    return "Hello";
  }
}

Let’s write a test for the above endpoint using org.springframework.boot.test.web.client.TestRestTemplate:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class DemoTest {
  @Autowired TestRestTemplate restTemplate;

  @Test
  public void testHello() {
    ResponseEntity<String> respEntity = restTemplate.getForEntity("/hello", String.class);
    assertThat(respEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
    assertThat(respEntity.getBody()).isEqualTo("Hello");
  }
}

Since you need to test the REST endpoint, we are starting the embedded servlet container by specifying the webEnvironment attribute of @SpringBootTest.

The default webEnvironment value is WebEnvironment.MOCK, which doesn’t start an embedded servlet container.

The following webEnvironment values are available:

  • MOCK (default) Loads a WebApplicationContext and provides a mock servlet environment. It will not start an embedded servlet container.
  • RANDOM_PORT Loads a ServletWebServerApplicationContext and starts an embedded servlet container listening on a random available port.
  • DEFINED_PORT Loads a ServletWebServerApplicationContext and starts an embedded servlet container listening on a defined port (server.port).
  • NONE Loads an ApplicationContext using SpringApplication but does not provide a servlet environment.

Generally speaking, while running the integration tests that start the embedded servlet containers, it is better to use WebEnvironment.RANDOM_PORT so that it won’t conflict with other running applications, especially in Continuous Integration (CI) environments where multiple builds run in parallel.

Testing Secured Controller/Service Methods

Spring boot provides several ways to test these secured endpoints. Before that, add the following dependencies to enable Spring Security and security testing capabilities:

<?xml version="1.0" encoding="UTF-8"?><project>
   <dependency>
           
      <groupId>org.springframework.boot</groupId>
           
      <artifactId>spring-boot-starter-security</artifactId>
       
   </dependency>
    
   <dependency>
           
      <groupId>org.springframework.security</groupId>
           
      <artifactId>spring-security-test</artifactId>
           
      <scope>test</scope>
       
   </dependency>
    
</project>

When you add the Security starter without custom security configurations, Spring Boot endpoints will be secured using HTTP basic authentication with a default user and generated password. To override that, you can configure credentials in application.properties as follows:

security.user.name=admin 
security.user.password=password 
security.user.role=USER,ADMIN

Let’s modify the above TestRestTemplate to test REST endpoints, passing the HTTP basic auth parameters, as follows:

@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public class DemoTest {
  @Autowired TestRestTemplate restTemplate;
  @Test public void testHello() throws Exception {
    ResponseEntity < String > respEntity = restTemplate.withBasicAuth("admin", "password").getForEntity("/hello", String.class);
    assertThat(respEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
    assertThat(respEntity.getBody()).isEqualTo("Hello");
  }
}

Note that you have passed the credentials using restTemplate.withBasicAuth(“admin”, “password”).

Also note that by using TestRestTemplate, you can invoke REST endpoints using relative paths like “/hello” instead of specifying the complete URL http://localhost:/ping