Skip to content

1grzyb1/controller-client

Repository files navigation

Controller Client for Spring Boot

Overview

Controller Client is a testing library tailored for Spring Boot applications, designed to streamline integration testing of REST controllers. Built on top of Spring MockMvc, it uses dynamic proxies to facilitate intuitive, concise, and type-safe testing, eliminating the repetitive boilerplate associated with MockMvc configurations. With Controller Client, you can directly call your controller methods in test cases, enabling fast and expressive tests that focus purely on application behavior.

Key Features

  • Dynamic Proxy for Controllers: Automatically creates proxies of controller classes, enabling direct, intuitive method calls for tests.
  • Flexible Request Customization: Provides hooks for configuring requests and responses, allowing fine-grained control over test conditions.
  • Type-Safe Response Mapping: Automatically maps JSON responses back to Java objects, supporting both single objects and lists based on the return type.

This library is still in early stage, so it doesn't support all spring web annotations.

Installation

  1. Add the following dependency to your build.gradle project:
    implementation 'io.github.1grzyb1:controller-client:1.0.9'
    
  2. Add -parameters to your Java compiler options to enable parameter names in the generated bytecode. For Gradle, you can do this by adding the following to your build.gradle file:
    compileJava {
       options.compilerArgs += ['-parameters']
    }

Example Usage

Basic Usage

Simple GET request

Create a proxy instance of your controller and invoke methods directly

Underneath it uses MockMvc to perform the request and map the response to the return type of the method.

@AutowireControllerClient
private ExampleController exampleController;
    
    @Test
    void basicGet() {
        var response = exampleController.exampleMethod();
        assertThat(response.message()).isEqualTo("Hello world!");
    }

Without controller client it would look like this

      @Autowired private MockMvc mockMvc;
      @Autowired private ObjectMapper objectMapper;

      @Test
      void basicGetMockMvc() throws Exception {
        String responseContent = mockMvc.perform(get("/example"))
                .andExpect(status().isOk())
                .andReturn()
                .getResponse()
                .getContentAsString();
    
        ExampleResponse response = objectMapper.readValue(responseContent, ExampleResponse.class);
    
        assertThat(response.message()).isEqualTo("Hello world!");
      }

POST request with request body

For following POST request

    @PostMapping("/example/body")
    public ExampleResponse examplePostMethod(@RequestBody ExampleRequest request) {
        return new ExampleResponse(request.getMessage());
}

Controller client usage would look like this

@AutowireControllerClient
private ExampleController exampleController;


@Test
void postWithBody() {
        var request = new ExampleRequest("Test message");
        var response = exampleController.bodyExample(request);
        assertThat(response.message()).isEqualTo("Received: Test message");
}

Without controller client it would look like this

    @Test
    void testBodyExample() throws Exception {
        var exampleRequest = new ExampleRequest("Test message");
        var requestJson = objectMapper.writeValueAsString(exampleRequest);

    var requestBuilder =
                MockMvcRequestBuilders.request(HttpMethod.POST, "/example/body")
                        .content(requestJson)
                        .contentType(MediaType.APPLICATION_JSON);
        var responseContent = mockMvc.perform(requestBuilder)
                .andExpect(status().isOk())
                .andReturn()
                .getResponse()
                .getContentAsString();

    var response = objectMapper.readValue(responseContent, ExampleResponse.class);
        assertThat(response.message()).isEqualTo("Received: Test message");
    }

Customize injected Controller Client

Sometimes you will need to add some sort of customization to injected proxy. To do this, you can provide class that implements ControllerClientAnnotationCustomizer.

For example let's say that you need to add csrf token too each request:

@AutowireControllerClient(customizer = CsrfControllerClientCustomizer.class)
private ExampleController exampleController;
    public class CsrfControllerClientCustomizer implements ControllerClientAnnotationCustomizer {
    
    @Override
    public ControllerClientBuilder<Object> customize(ControllerClientBuilder<Object> builder) {
        return builder.customizeRequest(request -> request.with(csrf()));
    }
}

You can check more examples in the example package.

About

Spring library to create controller proxy in tests

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages