There are two forms of testing - black-box testing and white-box testing. This is an explanation of how we will use those two approaches to the extent expected in this class. As an introductory course, we do not expect as rigorous of testing as a production software project would use. However, you should be familiar with this approach and use it in designing your tests for test driven development. Thinking about testing before you begin greatly reduces the chances of errors during the design and implementation process.
It is sometimes the case that both white-box tests and black-box tests result in the same tests. This occurs when someone thought through the black-box tests so well that the design was influenced by that thought process or when implementation details come up that should be ignored by the specification. When they do not match, that is when you need to think about what could be missing - is the understanding of the specification missing something, or is the code doing something not necessary, or is it just the fact that computers are coded in a certain way, so even though the specification didn't expose some detail, the implementation did?
For these explanations, we'll use the same problem: returning a character grade based on some's score. That is, if someone got 90 -> 100, then return 'A'. If someone got in the 80's, return 'B'. If someone got below 60, then return 'F'. If the score is over 100 or under 0, then report an error and return a space.
char score_to_grade(float score);One solution looks like this:
char score_to_grade(float score)
{
if (score > 100)
{
fprintf(stderr, "score_to_grade received %f, ",score);
fprintf(stderr, "expected 0 <= score <= 100\n");
return ' ';
}
else if (score >= 90)
{
return 'A';
}
else if (score >= 80)
{
return 'B';
}
else if (score >= 70)
{
return 'C';
}
else if (score >= 60)
{
return 'D';
}
else if (score >= 0)
{
return 'F';
}
else
{
fprintf(stderr, "score_to_grade received %f, ",score);
fprintf(stderr, "expected 0 <= score <= 100\n");
return ' ';
}
return ' ';
}
Black-box testing assumes that the design of test cases knows nothing about the internals of the implementation. Instead, the tests are designed based on what the code should do - the specification of the functions themselves.
We break down the functionality into groups of similar functionality based on the ranges. We know that over 100, it should give an error. From 90 through 100, it gives the same answer ('A'), 80 until 90 (exclusive), return ('B'), etc. So we have the following ranges.
| Input Domain | Output |
| (100,inf] | error, space |
| [90, 100] | 'A' |
| [80, 90) | 'B' |
| [70, 80) | 'C' |
| [60, 70) | 'D' |
| [0, 60) | 'F' |
| (-inf,80] | error, space |
White-box testing uses the code to design test cases. We want to make sure that every path through the code has a separate test case and that the conditionals are correct. Therefore, it has a relationship to the boundary cases above, but with the additional of looking at paths through the code. In this description, I am combining the path testing and conditional testing.
In the code above, each call goes through only a single if statement, so all of the paths are very simple. In fact, the paths match up exactly with the ranges we identified through test cases designed via the black-box testing approach. The conditional statements in the if statements specify the range of that if statement. We then create a test for each boundary of the if statements.
For example, we first look at the first if statement. The purpose of this is for all numbers over 100. Therefore, we choose a number 100, just over 100 and a much larger number and make test cases for those. (100, ?), (100.2, error/space), (200, error/space). We look at what is supposed to happen for 100 and fill that in (100, 'A').
Then we look at the second if statement (the first else if). We see it is for greater than or equal to 80. So then we choose 79.9 and 80 as the test cases, as well as a middle number (85). (79.9, ?), (80, 'B'), (85, 'B'). We look up what happens for 79.9 and choose (79.9, 'C').
Following this process, we will obtain a very similar set of test cases as we did for the black-box testing.