Skip to content

Latest commit

 

History

History
121 lines (93 loc) · 5.97 KB

T310-11122018.md

File metadata and controls

121 lines (93 loc) · 5.97 KB

LESSON 10 - TOPIC 3: Program Correctness and Testing

Accompanying code files:

Jump to Homework


As mentioned before, software testing is so important that there are roles in the software development world solely dedicated to it. Before you ship a product, it is extremely important that you test it rigorously to save a lot of time and effort fixing things later on (especially if really bad things happen if things malfunction e.g. a driverless car), and because clients prefer using a more polished product.

Program Correctness

  • It is important to note that just because a program passes all the tests written for it, does not necessarily mean that it is correct.
  • This is because it's not possible to test every possible set of input data, which is why it's important to cover all of the input boundaries.
  • While there are many mathematical techniques used to prove correctness in some cases, you will not be expected to know any of these for the AP exam.
  • However, you will need to be able to make assertions about the program state at many points during the program execution.

Assertions

  • An assertion is a precise predicate (T/F) statement about a program at any given point, that indicates if the statement is true at that point of execution.
  • The idea is that if the assertion is true at one point, then the program is working correctly at that point.
  • The assertion is the actual part of code in the program that tests the condition of the statement that is supposed to be true at that point.
  • This condition is known as the invariant. In the example below, the invariant during the execution of the for loop is that i is always less than 10 during the execution of the statements in the for loop.
for (int i = 0; i < 10; i++) {
  print(i); // for this part of the code, according to the loop invariant, any value that is >= 10 should never be printed
}
  • Writing correct algorithms involves being able to make accurate assertions about your code.

Preconditions

  • The precondition of any piece of code (a method, loop, or block) is a statement of what is true immediately before execution of that code.
  • In other words, when writing test cases, you should not need write tests for when the precondition is not met, because the precondition should have been met before program execution (and you can't test program execution without actually executing the program).
  • So when writing tests, preconditions specify the set-up required for the test case to be executed successfully (because writing incorrect test methods can create bugs too!)

Post-Conditions

  • The postcondition for any piece of code is a statement of what is true immediately after execution of the code.
  • In other words, your tests should be testing if the postcondition is met after code execution.

Types of Tests

UNIT TESTS

  • These are usually short methods that test whether or not a specific method is working correctly
  • These involve blackbox testing, i.e. you should know beforehand what the result should be when you run the method with a certain input, even if you don't know how the method should be implemented yet
  • These must call the actual method when testing, and at least one unit test should be written for each case of possible input data for the method
  • If the method produces correct values for each test case, it passes the unit test.
  • The example below is a unit test for the method getArea, of the Circle class, that produces the area of a Circle object based on its input radius.
public class Circle {

  public double radius;

  /** Constructor for Circle object
    * @param radius is the radius of the circle
    * PRECONDITION: radius is a non-negative number
    * POSTCONIDTION: a Circle object of radius input is created
    */
  public Circle(double radius) {
    this.radius = radius;
  }

  /** @return the radius of the Circle object
    * PRECONDITION: the Circle object being called on exists
    */
  public double getRadius() {
    return this.radius;
  }

  /** PRECONDITION: Circle object exists
    * @return the area of the Circle object based on its radius
    */
  public double getArea() {
    return Math.PI * Math.pow(radius, 2)
  }

}

public class UnitTestForCircle {

  public static void main(String[] args) {

    testGetArea(new Circle(10), 314.15926);
    testGetArea(new Circle(0), 0.0);

  }

  /** Unit test for the getArea method
    * @param c is the Circle object being tested
    * @param answer is the correct answer for when c.getArea() is called
    */
  public static void testGetArea(Circle c, double answer) {

    // Use a tolerance to determine closeness of answer
    double tolerance = 0.0001;

    if (Math.abs(c.getArea() - answer) <= tolerance) {
      System.out.println(c.getRadius() + " passed the test.");
    } else {
      System.out.println(c.getRadius() + " failed the test.")
    }
  }
}
  • If you run the code above, it should print out 10.0 passed the test. and 0.0 passed the test. in the console.

INTEGRATION TESTS

  • These are used to make sure that connections to outside resources are working properly
  • E.g. if you are writing software that connects to Facebook, then the test that checks if the connection is made correctly is the integration test.

HOMEWORK

Assignments:

  • Complete the tests for the VendingMachine.java and Drink.java classes - INVITATION
  • From the Program Design & Analysis Question Set, complete: 10, 12-14.
  • Answers will be posted on the Topic 3 assignment answers issue.

Prep for Next Class:

  • Bring laptop for next class