Unit testing with multiple collaborators

2019-07-20 00:33发布

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?

2条回答
我想做一个坏孩纸
2楼-- · 2019-07-20 00:48

zielaj's answer is sound, but you could also create a PostMethodFactory with the following signature:

PostMethod getInstance(String url, String message, String contentType);

... 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:

public PostMethod getInstance(String url, String content, String contentType, String charset) {
  PostMethod post = new PostMethod(url);
  RequestEntity entity = new StringRequestEntity(message, contentType, charset);
  post.setRequestEntity(entity);
  return post;
}
查看更多
祖国的老花朵
3楼-- · 2019-07-20 00:54

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

查看更多
登录 后发表回答