【问题标题】:How to write unit test of a static void method如何编写静态 void 方法的单元测试
【发布时间】:2020-10-27 15:59:47
【问题描述】:

我遇到一个问题,我不知道如何编写静态 void 方法的单元测试。

我现在有一个使用 Apache HttpClient 的 HttpHelper 类。如下代码。

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。

我想知道如何测试那些 void 方法。我的概念是在我的测试中创建一个CloseableHttpClient,然后调用HttpHelper.init() 来创建实际的。然后比较我的预期和实际的是否相同。我说的对吗?

由于变量和方法被声明为静态。编写单元测试有点困难。有很多帖子说将方法设为静态是一种不好的做法。但是在我的示例中,我不知道如何避免将它们声明为静态并保留单个 CloseableHttpClient 实例。

谢谢!

【问题讨论】:

  • 单元测试的痛苦通常表明代码有异味。也许将httpClient 传递给send()(这样httpClient 可以很容易地模拟),或者更改为非静态,并注入httpClient(这样httpClient 可以很容易地模拟)。
  • 您好,感谢您的回复。但如果我这样做。也许httpClient 不能保留单个实例?

标签: java unit-testing mockito httpclient junit5


【解决方案1】:

单实例保证主要通过单例模式解决。单元测试的一个常见技巧是创建一个具有受保护可见性的构造函数,您可以在其中放置用于测试的参数。这个类终于可以变成这样了。

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,但这是细节。

【讨论】:

    【解决方案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()
    

    附言

    如果您有某种可用的依赖注入框架,那么您可以完全摆脱静态方法。

    【讨论】:

      猜你喜欢
      • 2022-01-02
      • 1970-01-01
      • 2019-06-12
      • 1970-01-01
      • 1970-01-01
      • 2019-05-11
      • 1970-01-01
      • 2021-11-17
      • 2011-08-23
      相关资源
      最近更新 更多