如何基于HTTP连接测试代码

时间:2017-06-22 19:43:39

标签: java unit-testing

我正在研究一个解析从HTTP端点检索到的XML的系统。当我考虑如何测试我的代码时,我不希望我的单元测试实际上向实时站点发出HTTP请求。看起来好像很好。

所以我拿了读取端点内容的代码并将其包装在这个类中,所以我可以用Mockito来模拟它。但是现在,我该如何为这个类编写单元测试?我只是把问题推下来了,现在我仍然需要与之抗衡。

我可以再次包装URL对象 ,但我只是在推卸责任。

我正试图遵循“清洁代码”中的 3 TDD 法则

  • 第一法律:在您编写失败的单元测试之前,您可能无法编写生产代码。

  • 第二条法律:您可能不会写更多单元测试而不是足以失败。

  • 第三法律:您可能不会编写超过足以通过当前失败测试的生产代码。

我已经通过完成本课程违反了第一定律,但我不明白如何通过单元测试来解决这个问题。有什么建议吗?

/**
 * Fetches the content from an HTTP Resource
 */
public class HttpFetcher {

    /**
     * Gets the contents of an HTTP Endpoint using Basic Auth, similar to how Postman (chrome extenstion) does.
     *
     * @param username Username to authenticate with
     * @param password Password to authenticate with
     * @param url URL of the endpoint to read.
     * @return Contents read from the endpoint as a String.
     * @throws HttpException if any errors are encountered.
     */
    public String get(String username, String password, String url) {
        URLConnection connection;

        // Establish Connection
        try {
            connection = new URL(url).openConnection();
            String credentials = encodeCredentials(username, password);
            connection.setRequestProperty("Authorization", "Basic " + credentials);
        } catch (MalformedURLException e) {
            throw new HttpException(String.format("'%s' is not a valid URL.", e));
        } catch (IOException e) {
            throw new HttpException(String.format("Failed to connect to url: '%s'", url), e);
        }

        // Read the response
        try {
            String contents = readInputStream(connection.getInputStream());
            return contents;
        } catch (IOException e) {
            throw new HttpException(String.format("Failed to read from the url: '%s' ", url), e);
        }
    }

    private String encodeCredentials(String username, String password) {
        String credentials = String.format("%s:%s", username, password);
        String encodedCredentials = new String(Base64.encodeBase64(credentials.getBytes()));
        return encodedCredentials;
    }

    private String readInputStream(InputStream is) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
            return reader.lines().collect(Collectors.joining("\n"));
        }
    }
}

2 个答案:

答案 0 :(得分:1)

您可以将Connection的创建移动到某个外部类,然后模拟该类:

class HttpFetcher {
  private final ConnectionCreator connectionCreator;

  ...

  public HttpFetcher(ConnectionCreator connectionCreator) { this.connectionCreator = connectionCreator; }
  public String get(...) {
    ...
    try {
      connection = connectionCreator.createConnectionForUrl(url);
      ...

这也是单一责任原则的一个小改进:一类用于从连接获取数据,一类用于实际创建连接。

答案 1 :(得分:0)

我强烈建议您使用WireMock。这个工具可以用来精确地为这些类型的测试启动本地服务器。

此类测试的一个示例复制如下:

@Test
public void exampleTest() {
    stubFor(get(urlEqualTo("/my/resource"))
            .withHeader("Accept", equalTo("text/xml"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "text/xml")
                .withBody("<response>Some content</response>")));

    Result result = myHttpServiceCallingObject.doSomething();

    assertTrue(result.wasSuccessful());

    verify(postRequestedFor(urlMatching("/my/resource/[a-z0-9]+"))
            .withRequestBody(matching(".*<message>1234</message>.*"))
            .withHeader("Content-Type", notMatching("application/json")));
}