Search code examples
testingprivate-membersapache-commons-httpclient

How to test Apache HttpClient RequestConfig values are set correctly? No public getters present


I have this class to configure a HttpClient instance:

package com.company.fraud.preauth.service.feignaccertifyclient;

    import com.company.fraud.preauth.config.ProviderClientConfig;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.apache.http.ssl.SSLContextBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    import java.security.KeyManagementException;
    import java.security.KeyStoreException;
    import java.security.NoSuchAlgorithmException;


    @Slf4j
    @Configuration
    @RequiredArgsConstructor
    public class FeignClientConfig {

        private final ProviderClientConfig providerClientConfig;

        public HttpClient buildHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
            RequestConfig.Builder requestBuilder = RequestConfig.custom();
            requestBuilder.setConnectTimeout(providerClientConfig.getConnectionTimeout());
            requestBuilder.setConnectionRequestTimeout(providerClientConfig.getConnectionRequestTimeout());
            requestBuilder.setSocketTimeout(providerClientConfig.getSocketTimeout());
            SSLContextBuilder builder = new SSLContextBuilder();
            builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());

            return HttpClientBuilder.create()
                    .setMaxConnPerRoute(providerClientConfig.getMaxConnectionNumber())
                    .setDefaultRequestConfig(requestBuilder.build())
                    .setSSLContext(builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()).build())
                    .build();
        }
    }

How to unit test this class, to see into the resulted HttpClient that these values are correctly set?

From the httpClient I cannot get access to its RequestConfig.

I am aware of these two posts:

How do I test a private function or a class that has private methods, fields or inner classes? (the number of upvotes in this question shows that it is a concurrent and controversial topic in testing, and my situation may offer an example that why we should look into the inner state of an instance in testing, despite that it is private)

Unit test timeouts in Apache HttpClient (it shows a way of adding an interceptor in code to check configure values, but I don't like it because I want to separate tests with functional codes)

Is there any way? I understand that this class should be tested, right? You cannot blindly trust it to work; and checking it "notNull" seems fragile to me.


This link may point me to the right direction:

https://dzone.com/articles/testing-objects-internal-state

It uses PowerMock.Whitebox to check internal state of an instance.


Solution

  • So I have checked into PowerMock.Whitebox source code, and it turns out reflection is used internally. And, as PowerMock is said to be not compatible with JUnit 5 yet(till now), and I don't want to add another dependency just for testing, so I will test with reflection.

    package com.company.fraud.preauth.service.feignaccertifyclient;
    
    import com.company.fraud.preauth.config.PreAuthConfiguration;
    import com.company.fraud.preauth.config.ProviderClientConfig;
    import com.company.fraud.preauth.config.StopConfiguration;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.config.RequestConfig;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.Mock;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit.jupiter.SpringExtension;
    
    import java.lang.reflect.Field;
    
    import static org.hamcrest.CoreMatchers.equalTo;
    import static org.junit.Assert.assertThat;
    import static org.mockito.Mockito.when;
    
    @ExtendWith(SpringExtension.class)
    @SpringBootTest(classes = {
            PreAuthConfiguration.class,
            StopConfiguration.class,
    })
    public class FeignClientConfigTest {
    
        @Mock
        private ProviderClientConfig providerClientConfig;
    
        @Test
        @DisplayName("should return HttpClient with defaultConfig field filled with values in providerClientConfig")
        public void shouldReturnHttpClientWithConfiguredValues() throws Exception {
            // given
            when(providerClientConfig.getConnectionRequestTimeout()).thenReturn(30000);
            when(providerClientConfig.getConnectionTimeout()).thenReturn(30);
            when(providerClientConfig.getMaxConnNumPerRoute()).thenReturn(20);
            when(providerClientConfig.getSocketTimeout()).thenReturn(10);
            FeignClientConfig feignClientConfig = new FeignClientConfig(providerClientConfig);
    
            // when
            HttpClient httpClient = feignClientConfig.buildHttpClient();
    
            // then
            // I want to test internal state of built HttpClient and this should be checked
            // I tried to use PowerMock.Whitebox, but then I found it uses reflection internally
            // I don't want to introduce another dependency, and PowerMock is said not to be compatible with JUnit 5, so..
            Field requestConfigField = httpClient.getClass().getDeclaredField("defaultConfig");
            requestConfigField.setAccessible(true);
            RequestConfig requestConfig = (RequestConfig)requestConfigField.get(httpClient);
            assertThat(requestConfig.getConnectionRequestTimeout(), equalTo(30000));
            assertThat(requestConfig.getConnectTimeout(), equalTo(30));
            assertThat(requestConfig.getSocketTimeout(), equalTo(10));
        }
    
    }
    

    Also, I answer the first question in OP about when to test private members in a class here