Black Box Testing

Software Testing Part 1

Testing is a large part of software engineering. In fact, many software engineering teams have one or more person dedicated to writing tests for a system or individual class. Over the next few lessons we will be looking at how to properly test methods and classes to ensure they meets the specifications set forth.

Consider the following code for an implementation of a factorial calculation in a class called Factorial:

/**
 * Calculates the the value of n! by evaluating:
 *    n x (n-1) x (n-2) x ... x 3 x 2 x 1
 * Any invalid input value for n will result in a return value of -1.
 * Invalid inputs are values of n less than 0.
 * @param n This is the value of we are finding the factorial
 * @return int The value of the factorial calculated. Invalid inputs are
 */
public static int calculate(int n) {
    if (n <= 0) {
        return -1;
    }
    int value = 1;
    for (int i = n; i > 0; i --) {
        value *= i;
    }
    return value;
}

At first glance it may not be obvious there is anything wrong with the code, but it's arrogant to think that we are perfect, so we're going to write tests anyway.

Black Box Testing

Black box testing are sets of tests that are defined solely by the definition provided by the designer. In this case, the designer is me. We are now going to define tests based on the Javadoc provided above.

We must come up with a list of input values, both valid and invalid without looking at the code:

  • A negative, non-boundary integer (invalid)
  • A negative, boundary integer (-1) (invalid)
  • A non-1 positive integer (valid)
  • 0, a boundary case (valid)
  • 1, also a boundary case (valid)

These 5 cases cover generic cases and boundary cases equally. Errors in code tend to occur around the extreme values, also known as boundary cases. When choosing cases, just to be safe, try to choose boundary cases whenever possible.

Now we create a test harness (A separate class with the express purpose of testing something) and write our tests:

public class FactorialTestHarness {
    public static void main(String args[]) {
        // Blackbox Tests
        testFactorial("Negative Border Case (-1)", -1, -1);
        testFactorial("0", 0, 1);
        testFactorial("1", 1, 1);
        testFactorial("Non-1 Positive Integer", 5, 120);
        testFactorial("Invalid Number (-5)", -5, -1);
    }
    
    private static void testFactorial(String name, int input, int expected) {
        int output = Factorial.calculate(input);
        if (output == expected) {
            System.out.println("PASSED! " + name);
        } else {
            System.out.println("FAILED "+name+" with output: " + output);
        }
    }
}

If we run this, we will notice that all tests pass, except 1. The 0 test fails!

Exercise

Write a test harness and black box cases for the following method description:

/**
 * isValidPhone number accepts a String value representing a phone number as input.
 * The input string must follow the following rules:
 *     1. Be composed of an Area Code (Optional), Prefix (Required) and Suffix (required)
 *     2. The Area Code must be 3 digits and be in the range [200, 999]
 *     3. The Prefix must be 3 digits and be in the range (200, 999]
 *     4. The Suffix must be 4 digits.
 *     5. The Area code may optionally be surrounded by parenthesis
 *     6. The Area Code, Prefix and Suffix must be spaced apart by either a single space or a dash (-)
 *     7. If the Area Code is surrounded by parenthesis, it must be followed by a space. NOT a dash.
 * @param String phoneNumber The phone number string to be tested
 * @return boolean Return value is true if and only if the input String passes all 7 rules.
 *                      In all other cases, the value returned should be false.
 */

When you finish, write a method that passes all of your tests simultaneously.