Skip to main content
added 4 characters in body
Source Link
Mark Seemann
  • 234.3k
  • 51
  • 448
  • 778

I'm writing a Unit Test for a Class (CustomHttpClientCustomHttpClient) that internally uses a library (java.net.http.HttpClientjava.net.http.HttpClient) to make an external HTTP call.

Why a "CustomHttpClient"CustomHttpClient consumer should have the responsibility of injecting a dependency that could be internally handled by the module itself? Is it acceptable for testing purposes?

I'm writing a Unit Test for a Class (CustomHttpClient) that internally uses a library (java.net.http.HttpClient) to make an external HTTP call.

Why a "CustomHttpClient" consumer should have the responsibility of injecting a dependency that could be internally handled by the module itself? Is it acceptable for testing purposes?

I'm writing a Unit Test for a Class (CustomHttpClient) that internally uses a library (java.net.http.HttpClient) to make an external HTTP call.

Why a CustomHttpClient consumer should have the responsibility of injecting a dependency that could be internally handled by the module itself? Is it acceptable for testing purposes?

Source Link

TDD, Unit Test and mocks injection: What about the Single Responsability principle?

I'm writing a Unit Test for a Class (CustomHttpClient) that internally uses a library (java.net.http.HttpClient) to make an external HTTP call.

Being it a Unit Test, I would mock the internal HttpClient call (using a mocking framework like Mockito) returning a mocked response.

The Class to test has been designed to initialize the client library (that I want to mock) internally:

public class CustomHttpClient

    public HttpResonse doCall() {

       // Build Http Client
       HttpClient client = HttpClient.newBuilder().build();
       
       // Build request
       HttpRequest request = HttpRequest.newBuilder()
                .uri("http://myserviceuri")
                .GET()
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();

       // Do HTTP call --> TO MOCK WHEN UNIT TESTING
       return client.send(request,HttpResponse.BodyHandlers.ofString());
    }
}

For the way the class is designed, it is impossible to "replace" the HttpClient real instance with a mocked one during tests. Looking at the TDD, the used design effectively seems to break the principle that states that a class should be designed to be easily testable.

TDD suggests using Dependency injection to inject dependencies so that it can be easy to inject mocks when testing

According to this TDD principle, I see two different approaches:

  1. Passing the external library in the CustomHttpClient constructor
  2. Creating a Setter method to inject the external library
public class CustomHttpClient

    private HttpClient client;

    // External library injected on object creation
    public CustomHttpClient(HttpClient client){
       this.client = client;
    }

    public HttpResonse doCall() {
      
       // Build request
       HttpRequest request = HttpRequest.newBuilder()
                .uri("http://myserviceuri")
                .GET()
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();

       // Do HTTP call --> TO MOCK WHEN UNIT TESTING
       return client.send(request,HttpResponse.BodyHandlers.ofString());
    }
}

Anyway, both approaches seem to me to break the Single Responsibility principles and, at least, to be not elegant.

Why a "CustomHttpClient" consumer should have the responsibility of injecting a dependency that could be internally handled by the module itself? Is it acceptable for testing purposes?

Maybe I'm missing something and I really would appreciate any help :-)

P.S.: I also know that exists some Testing frameworks, like Powermocks, that are able to handle this particular use case, anyway I'm more interested in the correct design to use and why.

Thank you