CODE WITH SIBIN

Solving Real Problems with Real Code


Spring Boot – Testing REST Clients with @RestClientTest

In this comprehensive guide, we'll explore how to test REST clients in Spring Boot using @RestClientTest. This annotation simplifies testing REST clients by auto-configuring only the necessary components, making tests faster and more focused.

Table of Contents

  1. Introduction to @RestClientTest
  2. Setting Up a REST Client
  3. Writing Tests with @RestClientTest
  4. Mocking REST API Responses with MockRestServiceServer
  5. Advanced Testing Scenarios
  6. Best Practices
  7. Conclusion

1. Introduction to @RestClientTest

@RestClientTest is a Spring Boot test annotation that focuses only on testing REST clients. It:

  • Auto-configures Jackson/GSON support.
  • Provides a MockRestServiceServer to mock HTTP responses.
  • Excludes full auto-configuration, making tests faster.

When to Use @RestClientTest?

  • When testing a RestTemplate or WebClient-based REST client.
  • When you need to mock external API responses.
  • When you want lightweight tests without loading the entire Spring context.

2. Setting Up a REST Client

Let’s create a simple REST client that fetches user data from an external API.

Step 1: Define a User Model

public record User(Long id, String name, String email) {}

Step 2: Create a REST Client Service

@Service
public class UserService {

    private final RestTemplate restTemplate;

    public UserService(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    public User getUserById(Long id) {
        String url = "https://api.example.com/users/{id}";
        return restTemplate.getForObject(url, User.class, id);
    }
}

Step 3: Configure RestTemplate (Optional)

If you need custom configurations (e.g., timeouts, interceptors), define a RestTemplate bean:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                .setConnectTimeout(Duration.ofSeconds(5))
                .setReadTimeout(Duration.ofSeconds(5))
                .build();
    }
}

3. Writing Tests with @RestClientTest

Now, let’s test UserService using @RestClientTest.

Basic Test Structure

@RestClientTest(UserService.class)  // Focuses only on UserService and RestTemplate
@AutoConfigureWebClient(registerRestTemplate = true)
class UserServiceTest {

    @Autowired
    private MockRestServiceServer mockServer;  // Mock server for REST calls

    @Autowired
    private UserService userService;  // The service under test

    @Test
    void getUserById_ReturnsUser_WhenUserExists() throws Exception {
        // Mock API response
        mockServer.expect(requestTo("https://api.example.com/users/1"))
                .andRespond(withSuccess(
                        """
                        {
                            "id": 1,
                            "name": "John Doe",
                            "email": "john@example.com"
                        }
                        """,
                        MediaType.APPLICATION_JSON
                ));

        // Call the service
        User user = userService.getUserById(1L);

        // Verify
        assertThat(user.id()).isEqualTo(1L);
        assertThat(user.name()).isEqualTo("John Doe");
        mockServer.verify();  // Ensure expected requests were made
    }
}

Explanation

  • @RestClientTest(UserService.class) → Configures only UserService and RestTemplate.
  • MockRestServiceServer → Mocks the external API.
  • expect(requestTo(...)) → Defines expected request.
  • andRespond(withSuccess(...)) → Defines mock response.
  • mockServer.verify() → Ensures the expected request was made.

4. Mocking REST API Responses with MockRestServiceServer

MockRestServiceServer allows mocking different responses:

Mocking Success Response

mockServer.expect(requestTo("/users/1"))
        .andRespond(withSuccess(
                """
                {
                    "id": 1,
                    "name": "John Doe"
                }
                """,
                MediaType.APPLICATION_JSON
        ));

Mocking Error Response (404)

mockServer.expect(requestTo("/users/999"))
        .andRespond(withStatus(HttpStatus.NOT_FOUND));

Mocking Error Response (500)

mockServer.expect(requestTo("/users/1"))
        .andRespond(withServerError());

Matching Request Body (POST/PUT)

mockServer.expect(requestTo("/users"))
        .andExpect(method(HttpMethod.POST))
        .andExpect(content().json("""
            {
                "name": "Jane Doe"
            }
            """))
        .andRespond(withSuccess());

5. Advanced Testing Scenarios

Testing with Custom Headers

mockServer.expect(requestTo("/users/1"))
        .andExpect(header("Authorization", "Bearer token123"))
        .andRespond(withSuccess(...));

Testing Timeout Scenarios

@Test
void getUserById_ThrowsTimeoutException_WhenServerSlow() {
    mockServer.expect(requestTo("/users/1"))
            .andRespond(withTimeout());

    assertThrows(ResourceAccessException.class, () -> {
        userService.getUserById(1L);
    });
}

Using JSON Path Assertions

mockServer.expect(requestTo("/users"))
        .andExpect(jsonPath("$.name", is("John Doe")))
        .andRespond(withSuccess(...));

6. Best Practices

  1. Keep Tests Focused
    • Test only REST client logic, not business logic.
  2. Use MockRestServiceServer
    • Avoid real HTTP calls in tests.
  3. Verify Requests
    • Always call mockServer.verify() to ensure expected requests were made.
  4. Test Error Cases
    • Mock 4xx/5xx responses to ensure error handling works.
  5. Use @RestClientTest for REST Clients Only
    • For full integration tests, use @SpringBootTest.

7. Conclusion

@RestClientTest is a powerful tool for testing REST clients in Spring Boot. It:

  • Provides a lightweight test environment.
  • Uses MockRestServiceServer to mock HTTP responses.
  • Makes testing REST clients fast and reliable.

By following this guide, you can write efficient and maintainable tests for your REST clients.

Leave a Reply

Your email address will not be published. Required fields are marked *