1

I often find that I am unsure how to test code that needs to loop. Take for example the following:

methodUnderTest(inputQueue) {
    while (inputQueue.count > 0) {
        process(inputQueue.dequeue());
    }
}

The first test might look like this:

processesInput1() {
   // Arrange
   inputQueue = [a];

   // Act
   methodUnderTest(inputQueue);

   // Assert
   Assert.wasProcessed(a);
}

The logical next test is:

processesInput2() {
   // Arrange
   inputQueue = [a, b];

   // Act
   methodUnderTest(inputQueue);

   // Assert
   Assert.wasProcessed(a);
   Assert.wasProcessed(b);
}

but by the time we get to:

processesInput3() {
   // Arrange
   inputQueue = [a, b, c];

   // Act
   methodUnderTest(inputQueue);

   // Assert
   Assert.wasProcessed(a);
   Assert.wasProcessed(b);
   Assert.wasProcessed(c);
}

It all starts to feel a little redundant. A good piece of advice I once heard about TDD was to treat tests like a specification. At what point does the test specify that an input of N items will all be processed? How best to portray this in a test?

Jack Allan
  • 14,554
  • 11
  • 45
  • 57
  • It seems you cover all cases (can add 0 items case). If you encounter a bug later, make an extra case for that specific bug. – Peter Apr 14 '15 at 08:59

4 Answers4

4

I would agree with your assessment - 3 items feels like a redundant test (2 is already suffice for the case of N items). I would also argue that once you have the test for 2 items, that for 1 item is also redundant, however of course ends up there as a result of a disciplined use of TDD.

Coming from a specification perspective, a test for 0 items and a test for N items feels suffice, and therefore if you wanted to keep your test set minimal I would suggest deleting the test for 1 item.

robjohncox
  • 3,639
  • 3
  • 25
  • 51
3

I'd advise you to re-examine your original question. TDD encourages you to drive out an implementation, one baby step at a time, by specifying what should happen. I'd suggest that the word 'loop' is unnecessary in this (or pretty much any other) specification. Instead, I'd expect to see a specification that reads "All inputs should be processed." Now, the number of inputs in the queue (if that's what it is) is arbitrary - the requirement is that they should all be processed.

The choice of how whether to test different numbers of inputs comes down to your professional judgement of what the risk is (of a problem occurring) and what the benefit is (from writing extra tests). As Kent Beck said:

“I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence ... I suspect this level of confidence is high compared to industry standards”

Community
  • 1
  • 1
Seb Rose
  • 3,628
  • 18
  • 29
1

Generally, you want to test a happy path and boundary conditions.

Mathematically speaking you want to cover all equivalence classes. It means inputs that represent a set of meaningful use cases. For example empty, non empty and a full queue.

In your case test for 0 items and 2 items should suffice.

woru
  • 1,420
  • 9
  • 17
0

Like other answers said, it may be enough to test for zero and two arguments. However, you may want to try with 1000, or more. Then what?

At what point does the test specify that an input of N items will all be processed? How best to portray this in a test?

Now it depends whether the mocking framework supports such things, but in gmock you can use .WillRepeatedly(action), where in the action you can do additional processing (like checking array values in your specific example.

If you use gmock, you can also check just the number of calls to the mocked method using .Times(cardinality).

BЈовић
  • 62,405
  • 41
  • 173
  • 273