0

I have a service I am mocking like this:

@ExtendWith(MockitoExtension.class)
class MyServiceTest {

@InjectMocks
MyService myService;



    @Test
    void testSendRec() {
       myService.sendDocRec(.. pass params..);
    }

}

the service:

@Service
public class MyService {

    String sendDocRec( params ) {

       // builds request
       HttpUriRequestBase request = getRequest(  params );

        String response = doRequest(request);
    }

    public String doRequest(ClassicHttpRequest request) {
    String result = null;

        try (CloseableHttpClient httpclient = HttpClients.custom()
                .setConnectionManager(this.connectionManager)
                .setConnectionManagerShared(true) 
                .setDefaultRequestConfig(this.requestConfig)
                .build()) {

            final HttpClientContext clientContext = HttpClientContext.create();

            try (CloseableHttpResponse response = httpclient.execute(request, clientContext)) {

                result = EntityUtils.toString(response.getEntity());
            
            }
        } catch (URISyntaxException e) {
            log.error("Invalid URI {}", e);
        } catch (IOException e) {
            log.error("Failed to make HTTP Request {}", e);
        } catch (ParseException e) {
            log.error("Failed parsing response body {}", e);
        }

        return result;
    }

}

I need to be able to mock the "CloseableHttpResponse response = httpclient.execute(request, clientContext)", such that the "response" object is something I create ahead of time. I am hoping some mocking when/then constructs would work for this? I would grateful for ideas on how to do this. Thanks!

Timothy Clotworthy
  • 1,960
  • 2
  • 19
  • 42
  • You aren't mocking anything. Also as the `HttpClient` is configured inside the method you cannot mock it. Instead you should inject it that way you can mock it. Or use the spring `RestTemplate` or `WebClient` and configure that with Apache HTTP Client and use the testing/mocking support for that. – M. Deinum Nov 21 '22 at 16:03
  • [Why is my class not calling my mocked methods in unit test?](https://stackoverflow.com/q/74027324/112968) shares some useful insights (and a solution to your problem too). If you move the creation of your `HttpClient` to a different class an and instance of this class is injected into your class, testing becomes straightforward. – knittl Nov 27 '22 at 10:02

1 Answers1

0

You can't do it using the current code structure. httpclient object is getting created within the method under test. So it can't be mocked.

You need to delegate httpclient object creation to another method with belongs to a different class (something similar to HttpClientFactory class). That HttpClientFactory class should only be responsible for creating httpClient instances. If you need you can write separate unit test case for the class.

 @Inject
 private HttpClientFactory httpClientFactory;

      public String doRequest(ClassicHttpRequest request) {
        String result = null;
    
            try (CloseableHttpClient httpclient = httpClientFactory.getInstance()) {
    
                final HttpClientContext clientContext = HttpClientContext.create();
    
                try (CloseableHttpResponse response = httpclient.execute(request, clientContext)) {
    
                    result = EntityUtils.toString(response.getEntity());
                
                }
            } catch (URISyntaxException e) {
                log.error("Invalid URI {}", e);
            } catch (IOException e) {
                log.error("Failed to make HTTP Request {}", e);
            } catch (ParseException e) {
                log.error("Failed parsing response body {}", e);
            }
    
            return result;
        }

Now you can mock response like below:

 @Mock
 private HttpClientFactory httpClientFactory;

public String testDoRequest(ClassicHttpRequest request) {
....
    CloseableHttpClient mockedClient = mock(CloseableHttpClient.class);
    CloseableHttpResponse mockedResponse = mock(CloseableHttpResponse.class);
    when(httpClientFactory.getInstance()).thenReturn(mockedClient);
    when(mockedClient.execute(eq(request), any(clientContext))).thenReturn(mockedResponse);
....}
aatwork
  • 2,130
  • 4
  • 17
  • thank you, but I am confused about this. Is the @Inject and the doRequest() method you are showing a refactoring of doRequest() within the MyService class? – Timothy Clotworthy Nov 21 '22 at 16:44
  • also where is my sendDocRec() method that was the original called method? I have lost the context against my original question . Thanks – Timothy Clotworthy Nov 21 '22 at 17:09
  • first comment: answer is yes, refactording of doRequest() within MyService class. second comment: sendDocRec() would still be part of MyService & it will remain unchanged. i didn't add it as there was no change. Probably that caused confusion. I just wanted to show the parts I would change. – aatwork Nov 21 '22 at 17:21
  • you seemed to have omitted the ConnectionManager and the requestConfig...? Just way too much shorthand for me. Obviously you are assuming I have skills to fill the holes. Thanks anyway... – Timothy Clotworthy Nov 21 '22 at 17:33