Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TDD - Refactoring into black-box?

I have a nontrivial service object developed with TDD. It started with a simple task: For an object from queue, construct an attempt for asynchronous processing. So I wrote a test around my constructAttempt() method:

void constructAttempt() {...}

There are numerous possible scenarios that need to be taken into consideration, so I have a dozen tests for this method.


Then I implemented what I really needed it to do: Scan the whole queue and construct a batch of attempts. So the code looks more like:

public void go() {
    for (QueuedItem item : getQueuedItems()) {
        constructAttempt(item);
    }
}

So I added a new test or two for this go() method.


Finally I discovered I needed some preprocessing which sometimes may affect constructAttempt(). Now the code looks more like:

public void go() {
    preprocess();
    for (QueuedItem item : getQueuedItems()) {
        constructAttempt(item);
    }
}

I have a few doubts about what I should do now.

Shall I keep the code as is, with constructAttempt(), preprocess() and go() tested independently? Why yes/why not? I risk not covering side effects of preprocessing and break encapsulation.

Or shall I refactor my whole test suite to only call go() (which is the only public method)? Why yes/why not? This would make tests a little bit more obscure, but on the other hand it would take all possible interactions into consideration. It would in fact become a black-box test using only the public API, what may not be in line with TDD.

like image 896
Konrad Garus Avatar asked Jun 30 '10 14:06

Konrad Garus


1 Answers

The go method is really just orchestrating several interactions, and isn't very interesting in its own right. If you write your tests against go instead of your subordinate methods, the tests are likely be hideously complicated because you'll have to account for the combinatorial explosion of interactions between preprocess and constructAttempt (and maybe even getQueuedItems, though that sounds relatively simple).

Instead, you should write tests for the subordinate methods - and the tests for constructAttempt need to account for all of preprocess' potential effects. If you can't simulate those side-effects (by manipulating the underlying queue or a test double) refactor your class until you can.

like image 84
Jeff Sternal Avatar answered Sep 28 '22 03:09

Jeff Sternal