Unit Test for Buddi

From Expertiza_Wiki
Revision as of 00:01, 20 October 2009 by Kanke (talk | contribs)
Jump to navigation Jump to search

Unit Test for Buddi

CORRECT Boundary Conditions

Many bugs in code occur around "boundary conditions," that is, under conditions where the code's behavior may be different from the normal, day-to-day routine.

For instance, suppose you have a function that takes two integers:

public int calculate(int a, int b) {
  return a/(a+b);
}

Most of the time, this code will return a number just as you expect. But if the sum of a and b happens to equal zero, you will get an ArithmeticException instead of a return value. That is a boundary condition—a place where things might suddenly go wrong, or at least behave differently from your expectations.

To help you think of tests for boundary conditions, we'll use the acronym CORRECT:

  • Conformance — Does the value conform to an expected format?
  • Ordering — Is the set of values ordered or unordered as appropriate?
  • Range — Is the value within reasonable minimum and maximum values?
  • Reference — Does the code reference anything external that isn't under direct control of the code itself?
  • Existence — Does the value exist (e.g., is non-null, non-zero, present in a set, etc.)?
  • Cardinality — Are there exactly enough values?
  • Time (absolute and relative) — Is everything happening in order? At the right time? In time?

Let's look at each one of these in turn. Remember that for each of these areas, you want to consider data that is passed in as arguments to your method as well as internal data that you maintain inside your method and class.

The underlying question that you want to answer fully is:

   What else can go wrong?

Once you think of something that could go wrong, write a test for it. Once that test passes, again ask yourself, "what else can go wrong?" and write another test, and so on.

Conformance

Many times you expect or produce data that must conform to some specific format. An e-mail address, for instance, isn't just a simple string. You expect that it must be of the form:

   *
     name@somewhere.com

With the possibility of extra dotted parts:

   *
     firstname.lastname@subdomain.somewhere.com

And even oddballs like this one:

   *
     firstname.lastname%somewhere@subdomain.somewhere.com

Suppose you are writing a method that will extract the user's name from their e-mail address. You'll expect that the user's name is the portion before the "@" sign. What will your code do if there is no "@" sign? Will it work? Throw a runtime exception? Is this a boundary condition you need to consider?[1]

Validating formatted string data such as e-mail addresses, phone numbers, account numbers, or file names is usually straightforward. But what about more complex structured data? Suppose you are reading some sort of report data that contains a header record linked to some number of data records, and finally to a trailer record. How many conditions might we have to test?

   *
     What if there's no header, just data and a trailer?
   *
     What if there's no data, just a header and trailer?
   *
     What if there's no trailer, just a header and data?
   *
     What if there's just a trailer?
   *
     What if there's just a header?
   *
     What if there's just data?

Just as with the simpler e-mail address example, you have to consider what will happen if the data does not conform to the structure you think it should.

And of course, if you are creating something like an e-mail address (possibly building it up from different sources) or the structured data above, you want to test your result to make sure it conforms.

[1]E-mail addresses are actually very complicated. A close reading of RFC822 may surprise you.


Ordering

Another area to consider is the order of data, or the position of one piece of data within a larger collection. For instance, in the largest() example in the previous chapter, one bug manifested itself depending on whether the largest number you were searching for was at the beginning or end of the list.

That's one aspect of ordering. Any kind of search routine should be tested for conditions where the search target is first or last, as many common bugs can be found that way.

For another aspect of ordering, suppose you are writing a method that is passed a collection containing a restaurant order. You would probably expect that the appetizers will appear first in the order, followed by the salad (and that all-important dressing choice), then the entree and finally a decadent dessert involving lots of chocolate.

What happens to your code if the dessert is first, and the entree is last?

If there's a chance that sort of thing can happen, and if it's the responsibility of your method to deal with it if it does, then you need to test for this condition and address the problem. Now, it may be that this is not something your method needs to worry about. Perhaps this needs to be addressed at the user input level (see "Testing Invalid Parameters" later on).

If you're writing a sort routine, what might happen if the set of data is already ordered? Or worse yet, sorted in precisely reverse order? Ask yourself if that could cause trouble—if these are conditions that might be worth testing, too.

If you are supposed to maintain something in order, check that it is. For example, if your method is part of the GUI that is sending the dinner order back to the kitchen, you should have a test that verifies that the items are in the correct serving order:

public void testKitchenOrder() {

 Order order = new Order();
 FoodItem dessert = new Dessert("Chocolate Souffle");
 FoodItem entree  = new Entree("Beef Oscar");
 FoodItem salad   = new Salad("Tossed",
                              "Parmesan Peppercorn");
 // Add out of order
 order.addFoodItem(dessert);
 order.addFoodItem(entree);
 order.addFoodItem(salad);
 // But should come out in serving order
 Iterator itr = order.iterator();
 assertEquals(salad, itr.next());
 assertEquals(entree, itr.next());
 assertEquals(dessert, itr.next());
 // No more left
 assertFalse(itr.hasNext());

}

Of course, from a human factors standpoint, you'd need to modify the code so that it's flexible enough to allow people to eat their ice cream first, if so desired. In which case, you'd need to add a test to prove that your four-year old nephew's ice cream comes with everyone else's salads, but Grandma's ice cream comes at the end with your cappuccino.