Cara menulis unit test dari metode static void

Saya menghadapi masalah, saya tidak tahu cara menulis unit test metode static void.

Saya memiliki kelas HttpHelper yang menggunakan Apache HttpClient sekarang. Kode seperti di bawah ini.

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();
        }
    }
}

Jadi di main saya, saya akan memanggil HttpHelper.init() untuk menginisialisasi httpClient. Setiap kali saya ingin mengirim permintaan, saya akan menelepon HttpHelper.send(). Karena saya tidak ingin membuat httpClient baru setiap saat. Pada akhirnya, saya akan memanggil HttpHelper.close() untuk menutup httpClient.

Saya bertanya-tanya bagaimana cara menguji metode kosong tersebut. Konsep saya adalah membuat satu CloseableHttpClient dalam pengujian saya, lalu memanggil HttpHelper.init() untuk membuat yang sebenarnya. lalu bandingkan yang saya harapkan dan yang sebenarnya sama. Apakah saya benar?

Karena variabel dan metode dideklarasikan sebagai statis. Agak sulit untuk menulis unit test. Ada banyak postingan yang mengatakan membuat metode statis adalah praktik yang buruk. Namun dalam contoh saya, saya tidak tahu bagaimana cara menghindari mendeklarasikannya sebagai statis dan menyimpan satu instance CloseableHttpClient.

Terima kasih!


person rascee    schedule 27.10.2020    source sumber
comment
Rasa sakit pengujian unit biasanya menunjukkan bau kode. Mungkin meneruskan httpClient ke send() (sehingga httpClient dapat dengan mudah diejek), atau diubah menjadi non-statis, dan menyuntikkan httpClient (sehingga httpClient dapat dengan mudah diejek).   -  person Andrew S    schedule 27.10.2020
comment
Halo, terima kasih atas balasan Anda. Tapi jika aku melakukannya. mungkin httpClient tidak dapat menyimpan satu instance?   -  person rascee    schedule 27.10.2020


Jawaban (2)


Memiliki kelas yang sangat statis itu buruk, karena sangat sulit untuk mengujinya. Saya mengerti mengapa Anda menginginkan ini, tetapi Anda bisa mendapatkan semua manfaat yang sama seperti itu:

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();
        }
    }
}

Kemudian Anda dapat mengujinya seperti itu:


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"))
    }

}

dan menggunakannya dalam kode aktual seperti itu:

HttpHelper.getDeafultInstance().send()

P.S.

Jika Anda memiliki semacam kerangka injeksi ketergantungan, Anda dapat menghilangkan metode statis sama sekali.

person Tarmo    schedule 27.10.2020

Jaminan contoh tunggal sebagian besar diselesaikan dengan pola Singleton. Trik umum untuk pengujian unit adalah membuat konstruktor dengan visibilitas terlindungi tempat Anda dapat meletakkan argumen untuk pengujian. Kelas akhirnya bisa terlihat seperti ini.

public class HttpHelper {
    private static HttpHelper INSTANCE = new HttpHelper();

    public static HttpHelper getInstance() {
        return INSTANCE;
    }


    private CloseableHttpClient httpClient;

    private HttpHelper() {
        SSLContext sslContext = getDummySSL();
        this(HttpClients.custom().setSSLContext(sslContext).build(), sslContext);
    }

    protected HttpHelper(CloseableHttpClient httpClient, SSLContext sslContext) {
        this.httpClient = httpClient;
    }

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

    private static SSLContext getDummySSL() {
        ...
    }

    private void send() {
        ...
    }
}

Saya juga akan mengganti nama getDummySSL menjadi createDummySSL tetapi ini detailnya.

person Marc H.    schedule 27.10.2020