วิธีเขียน Unit Test ของวิธี Static void

ฉันกำลังประสบปัญหา ฉันไม่รู้วิธีเขียน unit test ของวิธี static void

ฉันมีคลาส HttpHelper ที่ใช้ Apache HttpClient ตอนนี้ รหัสเหมือนด้านล่าง

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

ดังนั้นในเมนูหลักของฉัน ฉันจะเรียก HttpHelper.init() เพื่อเริ่มต้น httpClient ทุกครั้งที่ฉันต้องการส่งคำขอ ฉันจะโทร HttpHelper.send() เพราะฉันไม่ต้องการสร้าง httpClient ใหม่ทุกครั้ง ในตอนท้ายฉันจะโทร HttpHelper.close() เพื่อปิด httpClient

ฉันสงสัยว่าจะทดสอบวิธีโมฆะเหล่านั้นได้อย่างไร แนวคิดของฉันคือการสร้าง CloseableHttpClient หนึ่งอันในการทดสอบ จากนั้นโทร HttpHelper.init() เพื่อสร้างอันจริง จากนั้นเปรียบเทียบอันที่ฉันคาดหวังกับอันจริงที่เหมือนกัน ฉันถูกไหม?

เนื่องจากตัวแปรและวิธีการประกาศเป็นแบบคงที่ การเขียน Unit Test ค่อนข้างยาก มีโพสต์มากมายที่กล่าวว่าการทำให้วิธีการคงที่เป็นแนวทางปฏิบัติที่ไม่ดี อย่างไรก็ตาม ในตัวอย่างของฉัน ฉันไม่รู้วิธีหลีกเลี่ยงการประกาศว่าเป็นแบบคงที่และเก็บอินสแตนซ์ CloseableHttpClient ไว้เพียงอินสแตนซ์เดียว

ขอบคุณ!


person rascee    schedule 27.10.2020    source แหล่งที่มา
comment
ความเจ็บปวดจากการทดสอบหน่วยมักจะบ่งบอกถึงกลิ่นรหัส บางทีอาจส่ง httpClient ไปที่ send() (เพื่อให้ httpClient สามารถจำลองได้ง่าย) หรือเปลี่ยนเป็นแบบไม่คงที่ และฉีด httpClient (เพื่อให้ httpClient สามารถจำลองได้ง่าย)   -  person Andrew S    schedule 27.10.2020
comment
สวัสดีขอบคุณสำหรับการตอบกลับ แต่ถ้าฉันทำอย่างนั้น บางที httpClient ไม่สามารถเก็บอินสแตนซ์เดียวได้   -  person rascee    schedule 27.10.2020


คำตอบ (2)


การมีคลาสแบบคงที่นั้นไม่ดี เพราะมันยากมากที่จะทดสอบสิ่งนี้ ฉันเข้าใจว่าทำไมคุณถึงต้องการสิ่งนี้ แต่คุณสามารถได้รับประโยชน์ทั้งหมดเหมือนกัน:

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

จากนั้นคุณสามารถทดสอบหน่วยได้ดังนี้:


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

}

และใช้ในโค้ดจริงเช่นนั้น:

HttpHelper.getDeafultInstance().send()

P.S.

หากคุณมีเฟรมเวิร์กการพึ่งพาการฉีดบางประเภท คุณสามารถกำจัดวิธีการแบบคงที่ได้เลย

person Tarmo    schedule 27.10.2020

การรับประกันอินสแตนซ์เดี่ยวส่วนใหญ่จะแก้ไขได้ด้วยรูปแบบซิงเกิลตัน เคล็ดลับทั่วไปสำหรับการทดสอบหน่วยคือการสร้าง Constructor ที่มีการป้องกันการมองเห็น ซึ่งคุณสามารถใส่อาร์กิวเมนต์สำหรับการทดสอบได้ ในที่สุดชั้นเรียนก็จะเป็นแบบนี้

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() {
        ...
    }
}

ฉันจะเปลี่ยนชื่อ getDummySSL เป็น createDummySSL ด้วย แต่นี่คือรายละเอียด

person Marc H.    schedule 27.10.2020