我正在研究一个解析从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"));
}
}
}
答案 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")));
}