Search code examples
javaunit-testingmockitohttpclientjunit5

How to write unit test of a static void method


I am facing a problem, I don't know how to write unit test of static void method.

I have a HttpHelper class that using Apache HttpClient right now. Code like below.

public class HttpHelper {
    private static CloseableHttpClient httpClient;

    public static void init() {
        httpClient = HttpClients.custom().setSSLContext(getDummySSL()).build();
    }

    public static void closeHttpClient() throws IOException {
        httpClient.close();
    }

    private static SSLContext getDummySSL() {
        ...omit
    }

    private static void send() {
        HttpGet httpGet = new HttpGet("https://someUrl.com");

        try(CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
            if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                responseString = EntityUtils.toString(httpResponse.getEntity());
                // do something
            } else {
                throw new Exception();
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

So in my main I will call HttpHelper.init() to initialize the httpClient. Everytime I wanna send a request, i will call HttpHelper.send(). Because I don't want to create a new httpClient everytime. At the end, I will call HttpHelper.close() to close the httpClient.

I am wondering how to test those void methods. My concept is to create one CloseableHttpClient in my test, then call HttpHelper.init() to create the actual one. then compare my expected one and actual one is same. Am I right?

Due to the variable and methods are declare as static. It is a bit difficult to write unit tests. There are many posts said making methods static is a bad practice. However in my example, I don't know how to avoid declaring them as static and keep a single CloseableHttpClient instance.

Thank you!


Solution

  • Having a class so static is bad, because it's very hard to test this. I understand why you would want this, but you could have all the same benefits like that:

    public class HttpHelper {
    
        private static HttpHelper DEFAULT_INSTANCE = null;
    
        private CloseableHttpClient httpClient;
    
        public HttpHelper(CloseableHttpClient httpClient) {
            this.httpClient = httpClient;
        }
    
        public static void getDeafultInstance() { // this should probably be synchronised for thread safety
            if (DEFAULT_INSTANCE == null) {
                DEFAULT_INSTANCE = httpClient = HttpClients.custom().setSSLContext(getDummySSL()).build();
            }
            return DEAFULT_INSTANCE;
        }
    
        private static SSLContext getDummySSL() {
            ...omit
        }
    
        public void closeHttpClient() throws IOException {
            httpClient.close();
        }
    
        private void send() {
            HttpGet httpGet = new HttpGet("https://someUrl.com");
    
            try(CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
                if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    responseString = EntityUtils.toString(httpResponse.getEntity());
                    // do something
                } else {
                    throw new Exception();
                }
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    Then you could unit test it like that:

    
    public class HttpHelperTest {
    
        @Test
        public testSendsRequestToSomeUrl() {
            CloseableHttpClient httpClientMock = mock();
            when(httpClient.execute(any())).thenReturn(..http_response_where_stauts_code_is_ok..)
            HttpHelper httpHelper = new HttpHelper(httpClientMock)
            httpHelper.send()
            verify(httpClient).execute(new HttpGet("https://someUrl.com"))
        }
    
    }
    

    and use it in actual code like that:

    HttpHelper.getDeafultInstance().send()
    

    P.S.

    If you have some kind of dependency injection framework available, then you could get rid of the static methods at all.