Lab: Testing

This laboratory’s exercises provide initial practice with testing C programs (without a debugger). Download the starter code for this lab: testing.tar.gz and extract it with the following terminal commands:

$ mkdir -p csc161/labs/testing
$ cd csc161/labs/testing
$ tar xvzf ~/Downloads/testing.tar.gz

As with the functions lab, this lab includes a Makefile, so you can run make to compile everything or make ___ where ___ is the name of the one program you want to compile. Again, we’ll discuss Makefiles in depth later in the course; for now, this is just a tool that can help you work a little more efficiently.

A. The assert Function in C

When developing programs, there are many ways to test your code. The simplest method is to simply run your code, printing variables to the terminal if you need to know what they contain. This works, but we have additional tools available to us.

The first we will look at is the assert macro in the assert.h library (you will need to #include <assert.h>). The macro assert(int) will immediately terminate the program if its parameter evaluates to false (0). It will also print the expression evaluated to the console to give context to the error. For example, if you test a > b in foo and it is the case that a <= b, the program will print “foo: Assertion a > b failed.”

Exercises

Review the code in quadratic.c. Pay close attention to the comments to understand what each function is supposed to do. Then modify the code in quadratic.c to use the assert macro so that it will test the following preconditions:

  1. The given precondition for the function computeDiscriminant.
  2. The precondition that a must not be zero in the function printRoots.

B. Choosing Test Cases

Recall that black-box testing is when a problem is examined to determine the logical cases that might arise. Test cases are developed without reference to details of code. For example, when testing a program that calculates \(a^b\), it would be sensible to test small numbers, big numbers, positive numbers, negative numbers, fractional numbers, whole numbers, and zero values in every combination.

In contrast, white-box testing is when the code is examined to determine each of the possible conditions that may arise, and tests are developed to exercise each part of the code. To continue the previous example would require explicit code for this calculation, which we do not provide here. However, it is likely that one would calculate \(a^b\) (for integer \(a\) and \(b\) values) by computing \(a^1\), \(a^2\), \(a^4\), \(a^8\), etc… and adding together the terms with exponents that sum to \(b\). It would then be good to test a range of \(a\) values as well as: powers of 2 for \(b\), non-powers of 2 for \(b\), \(b = 0\), etc…

Exercises

The code in reduce.c is supposed to reduce a fraction, with integers entered as numerator and (positive) denominator separately, to its simplest form.

  1. Develop two test plans for this program to determine whether the program works correctly. Apply both black-box and white-box testing strategies by identifying test cases that will cover a full range of situations that might be encountered in executing the program.
  2. Run reduce.c with all the cases from your test plan to determine whether the program works correctly.
  3. Fix all the errors you found in the program.
  4. Run the program again with all the cases from your test plan to be sure that it now works correctly.