Today I ran into a very difficult TDD problem. I need to interact with a server through HTTP POSTs. I found the the Apache Commons HttpClient, which does what I need.
However, I end up with a bunch of collaborating objects from Apache Commons:
public void postMessage(String url, String message) throws Exception {
PostMethod post = new PostMethod(url);
RequestEntity entity = new StringRequestEntity(message,
"text/xml; charset=ISO-8859-1");
post.setRequestEntity(entity);
HttpClient httpclient = new HttpClient();
try {
int result = httpclient.executeMethod(post);
System.out.println("Response status code: " + result);
System.out.println("Response body: ");
System.out.println(post.getResponseBodyAsString());
} finally {
post.releaseConnection();
}
}
I have a PostMethod
object, a RequestEntity
object and a HttpClient
object. I feel relatively comfortable passing in the HttpClient
ala dependency injection, but what do I do about the other collaborators?
I could create a bunch of factory methods (or a factory class) to create the collaborators, but I'm a bit afraid that I'd be mocking too much.
Follow Up
Thanks for the answers! My remaining issue is a method like this:
public String postMessage(String url, String message) throws Exception {
PostMethod post = new PostMethod(url);
RequestEntity entity = new StringRequestEntity(message,
"text/xml; charset=ISO-8859-1");
post.setRequestEntity(entity);
HttpClient httpclient = new HttpClient();
httpclient.executeMethod(post);
return post.getResponseBodyAsString();
}
How do I correctly verify that the returned value is from post.getResponseBodyAsString()
? Would I have to mock post
as well as client
?
zielaj's answer is sound, but you could also create a PostMethodFactory with the following signature:
... then use DI to inject both it and the HttpClient. Then you'd only have two things to mock.
The PostMethodFactory could be implemented thus:
Short answer: mock HttpClient, don't mock PostMethod or RequestEntity.
Of course this is a judgement call, but I'd suggest start with mocking things that really need to be mocked: HttpClient. PostMethod and RequestEntity are stack-local, fast, and deterministic, I'd leave them as they are, you can always mock them later if necessary. As your code is now, by mocking PostMethod and RequestEntity you complicate your api, complicate the code that uses your api, and expose the details of your implementation.
As your code evolves, you'll have a better idea what needs to be mocked, no need to try to predict the future now.
This might be useful:
http://www.testingreflections.com/node/view/7417