0

This probably sounds like a dumb question, forgive me if I'm asking this in the wrong place, I'd be happy to delete it if so. I'm sort of new to unit testing and I'd like to learn best practices regarding this question, it's been bugging me for a while.

Assume that there is a method which takes an array as the input and performs some algorithm to its elements. There may be a situation where the method is called repeatedly with input containing elements that have already been processed by the object (and remembered somewhere). I'd like to test that the method doesn't process the same input elements twice, i.e. that it does nothing the second time it encounters those items.

The object under test is actually a wrapper around another object that is provided as a depencency to it, so I can mock that object and verify that the necessary processing methods are called.

So my question is:

How many input elements would you consider to be "enough" to in order to test that the method works properly (for both old and new elements)? One? Three? Ten?

  • 2
    The answer to that question depends on what "works properly" means. – Robert Harvey Nov 12 '15 at 4:46
  • 1
    What algorithm? What data structures? Do you have a concrete example of what you are talking about? I am a bit unclear as to what, specifically, you are asking. – user22815 Nov 12 '15 at 4:53
1

What you describe is something of the form:

for elt in list loop
  if unprocessed(elt) then process(elt) end loop
end loop

What you REALLY have is something of the form

for elt in list loop
   maybe_process(elt)
end loop

where

proc maybe_process(elt) is
   if unprocessed(elt) then process(elt) end if
end proc

LISPers will immediately recognize the second form as MAPC, which takes a function and applies it to each element of a list.

At that point, your original problem is decomposed into two much simpler problems, that of calling an ARBITRARY function on each element of a list, and that of writing a function that can take an element, determine whether it has already been processed, and, if not, process it. The first function takes as few elements in the list as you need, I'd probably use three. The second function takes as many individual test cases as you need to check out the two pieces of the pie.

Even if you decompose it in the first form, you can use very simple test functions to check out the loop, and then a very simple process() function to check out the unprocessed() predicate.

The key is that you can use MUCH SIMPLER functions to do your testing phases.

  • I should mention in passing that doing it this way, with the decomposition explicit in the code, can get you in hot water with your management if your coworkers have never been exposed to "Structure and Interpretation of Computer Programs". Been there, done that, got the scars, had to write a full page of comments explaining the basics of continuation-passing style. – John R. Strohm Nov 12 '15 at 4:24
  • Thank you for such a detailed answer. Are you saying that the processing logic should be moved out of the loop into a separate function/method and the loop and the new method should be tested independently? – iosdude Nov 12 '15 at 4:32
  • PRECISELY. It simplifies the BLEEP out of the testing problem. This is why they taught you about subroutines in first-semester programming class! – John R. Strohm Nov 12 '15 at 4:33
  • OK, that makes sense. If you don't mind, can you please suggest the best way of separating them? Since I can't modify the class that is processing the elements, and, if I understood unit testing correctly, creating a private method for that is not an option because private methods aren't supposed to be tested, do I make a new class/function for processing a single element or there are better options? – iosdude Nov 12 '15 at 4:40

Not the answer you're looking for? Browse other questions tagged or ask your own question.