JMU JMU - Department of Computer Science
Help Tools
Lab: Experimenting with Unit Testing and Coverage


Getting Ready: Before going any further, you should:

  1. Setup your development environment.
  2. Download the following files:
    to an appropriate directory/folder. (In most browsers/OSs, the easiest way to do this is by right-clicking/control-clicking on each of the links above.)

1. A Review of System Testing: This part of the lab will review some of what you already know about system testing (i.e., testing a complete program/application).
  1. Execute RectangleArea with inputs 10.5 and 13.2.
  2. Assuming the prompts, other messages, and format are correct, is the output correct?


    Yes.
    Expand
  3. Change the format specifier from the correct value of 8.2 to the incorrect value of 7.2 and re-run the program with the same inputs.
  4. You know that the output is not correct (because the specifier is incorrect). Is it easy to see that the output is incorrect?


    No.
    Expand
  5. In the lab on "Using a Command Shell" you used a testing "trick" to help make it easier to detect defects of this kind. What was it?


    I used output redirection to capture the output of the program and then used a file comparison utility (like Meld or Kdiff3) to compare the actual output with the expected output.
    Expand
  6. In the lab on "Using a Command Shell" you also used a testing "trick" to help make it easier to run system tests without having to type the inputs each time. What was it?


    I used input redirection to send a file to the console.
    Expand
  7. What would you need to type at the command line to use both of the two "tricks" above with the input file you downloaded named test01.inp?


    java RectangleCalculator < test01.inp > test01.act
    
    Expand
  8. Just for practice, do it and then compare the expected output (in the file named test01.exp) and the actual output.
  9. Change the format specifier back to 8.2 and make sure the output is now correct.
  10. System testing that is performed by developers and/or professional testers is called alpha (\(\alpha\)) testing. System testing that is performed by actual or potential users is called beta (\(\beta\)) testing. Have you just acted as an alpha tester or a beta tester?


    An alpha tester.
    Expand
2. Unit Testing without a Test Harness: As you know, unit testing involves the testing of individual modules (a.k.a., units) in isolation. This part of the lab will help you understand different aspects of conducting unit testing without a test harness.
  1. What methods in the Geometry class are tested in GeometryDriver.java and how many unit tests does it contain?


    There are two different tests for the method anglesIn().
    Expand
  2. Add two unit tests for the rectangleArea() method (one of which must use the same values as the system test above).
  3. What code did you add?


    
            double area, height, width;        
            JMUConsole.print("Testing rectangleArea()\n");
            JMUConsole.print("-----------------------\n");
    
            width  = 10.5;
            height = 13.2;
            area   = Geometry.rectangleArea(width, height);
            JMUConsole.printf("rectangleArea(%5.2f, %5.2f): %5.2f\n", 
                              width, height, area);
    
            width  = 20.0;
            height = 15.0;
            area   = Geometry.rectangleArea(width, height);
            JMUConsole.printf("rectangleArea(%5.2f, %5.2f): %5.2f\n", 
                              width, height, area);
    
    Expand
  4. Run GeometryDriver.
  5. Is the output correct?


    It is, but it took a lot of work to be sure.
    Expand
  6. Suppose you were to work on something else for a week and then re-run GeometryDriver. Without looking at the source code, would you be able to tell if the output was correct or not? Why or why not?


    I certainly wouldn't. I would have to redo all of the calculations.
    Expand
  7. Modify GeometryDriver so that it prints both the expected value and the actual value for each test.
  8. What code did you add?


    As an example, for the first test of the rectangleArea() method, I changed the code to:
            width  = 10.5;
            height = 13.2;
            area   = Geometry.rectangleArea(width, height);
            JMUConsole.printf("rectangleArea(%5.2f, %5.2f) Expected: %5.2f\n", 
                              width, height, 138.60);
            JMUConsole.printf("rectangleArea(%5.2f, %5.2f) Actual:   %5.2f\n", 
                              width, height, area);
    
    Expand
  9. Suppose GeometryDriver contained 100 tests. Would you want to read through all of the output to see if there were any failures?


    I certainly wouldn't -- I'm lazy!
    Expand
  10. Modify the first test of anglesIn() so that it only generates output when there is a failure.


    I added an int variable named expectedAngles and then changed the code to the following:
            sides  = 3;
            expectedAngles = 3;        
            angles = Geometry.anglesIn(sides);
            if (angles != expectedAngles)
            {
                JMUConsole.printf("anglesIn(%d) Expected: %d\n", sides, expectedAngles);
                JMUConsole.printf("anglesIn(%d) Actual:   %d\n", sides, angles);
            }
    
    Expand
  11. What would you need to account for if you did a similar thing for the tests of rectangleArea()? (Hint: Think about performing arithmetic operations on double values.)


    It is never a good idea to use the relational operators == and/or != with double values because of numerical inaccuracies that can arise when operating on double values.
    Expand
  12. Suppose you were to do a similar thing for all of the tests in GeometryDriver. From a design perspective (not a testing perspective), what would be wrong with the GeometryDriver class?
    There would be a lot of duplicate code. That is, there would be similar if statements in every test.
    Expand
  13. How can you correct the problem identified in the previous question?
    Create a method (or two).
    Expand
3. Building a Simple Harness for Unit Testing: This part of the lab will help you build a simple harness for unit testing and understand the advantages of using such a tool.
  1. Create a class named Test that contains no methods.
  2. What code did you add?


    /**
     * A simple unit testing harness. Note: The methods in this class
     * assume that the JMUConsole has alread been opened.
     *
     * @author  Prof. David Bernstein, James Madison University
     * @version 1.0
     */
    public class Test {
    }
    
    Expand
  3. Add a method to the Test class with the following signature:
        public static void forEqualInt(int expected, int actual, String description)
        

    that prints the description if the values of expected and actual are not the same.

  4. What code did you add?


        /**
         * Display an alert if the actual value is not equal to the
         * expected value.
         *
         * @param expected     The expected value
         * @param actual       The actual value
         * @param description  A description of the test
         */
        public static void forEqualInt(int expected, int actual, String description) {
            if (expected != actual) {
                JMUConsole.printf("%s Expected: %d, Actual %d\n", 
                                  description, expected, actual);
            }
        }
    
    Expand
  5. Change the tests of the anglesIn() method in the GeometryDriver class so that they use this method.
  6. What does the code look like now?


            Test.forEqualInt(3, Geometry.anglesIn(3), "anglesIn(3)");
            Test.forEqualInt(4, Geometry.anglesIn(4), "anglesIn(4)");
    
    Expand
  7. Add a method to the Test class with the following signature:
        public static void forEqualDouble(double expected, double actual, 
                                          double tolerance,
                                          String description)
        

    that prints the description if the difference between the double values expected and actual is not within the given tolerance. Hint: Use Math.abs() to find the absolute value.

  8. What code did you add?


        /**
         * Display an alert if the actual double value and the expected double
         * value differ by more than the given tolerance.
         *
         * @param expected     The expected value
         * @param actual       The actual value
         * @param tolerance    The tolerance
         * @param description  A description of the test
         */
        public static void forEqualDouble(double expected, double actual,
                                          double tolerance,
                                          String description) {
            double difference;
            
            difference = Math.abs(expected - actual);
            if (difference > tolerance) {
                JMUConsole.printf("%s Expected: %5.2f, Actual %5.2f\n", 
                                  description, expected, actual);
            }
        }
    
    Expand
  9. Change the tests of the rectangleArea() method in the GeometryDriver class so that they use this method (with a tolerance of 0.01).
  10. What does your code look like now?


            Test.forEqualDouble(138.60, Geometry.rectangleArea(10.5, 13.2),
                                0.01, "rectangleArea(10.5, 13.2)");
            Test.forEqualDouble(300.00, Geometry.rectangleArea(20.0, 15.0),
                                0.01, "rectangleArea(20.0, 25.0)", );
    
    Expand
  11. If your code is correct, what output should be generated by GeometryDriver


    None.
    Expand
  12. Execute GeometryDriver and make sure this is the case.
  13. Modify the rectangleArea() method in the Geometry class so that it incorrectly calculates the area as the width times the width.
  14. Execute GeometryDriver.
  15. What output was generated?


    rectangleArea(10.5, 13.2) Expected: 138.60, Actual 110.25
    rectangleArea(20.0, 25.0) Expected: 300.00, Actual 400.00
    
    Expand
  16. Fix the error in the Geometry class.
  17. Do you think you could come back a month from now, rerun the tests on the Geometry class, and understand the output?


    Yes
    Expand
  18. Can the test harness you just wrote be used to test other classes?


    Yes, that's the beauty of putting the methods in the Test class rather than the GeometryDriver class.
    Expand

Copyright 2020