Project 3: A Unit Testing Framework

COMP 150-SEN, Spring 2019

Main project due: Tue, Apr 9 @ 11:59pm

In this project, you will develop a number of useful testing framework components. First, you will implement your own version of JUnit. Second, you will extend your JUnit to support QuickCheck-style random test generation (though to make this gradable we won't use randomness).

You will undoubtedly need to use java.lang.reflect in this project. Note that there are no shared test cases for this project.

Here is a code skeleton for this project.

Your first task is to implement the following method in class Unit:

    public static HashMap<String, Throwable> testClass(String name);
Given a class name, this method should run all the test cases in that class. The return value is a map where the keys of the map are the test case names, and the values are either the exception or error thrown by a test case (indicating that test case failed) or null for test cases that passed.

Here are the rules your test execution engine should follow:

After implementing the core JUnit functionality, your next step is to implement a fluent assertion API, in which conjunctions of assertions about objects are written using method chaining. For example, using this interface, we should be able to write

  String s = ...;
  Assertion.assertThat(s).isNotNull().startsWith("COMP");
meaning that s is not null and begins with "COMP".

We've started you off by creating a class Assertion defining several assertThat methods, but you'll need to create other classes to represent the result of calling the different assertThat methods. Here's how your interface should work:

QuickCheck is an automated program testing technique developed originally for Haskell. The idea of QuickCheck is that the programmer specifies a test case (called a property) that takes some parameters. The QuickCheck infrastructure runs that test cases repeatedly with random choices of parameters. For example, using the implementation for this project, we will be able to write the following property:

@Property
boolean absNonNeg(@IntRange(min=-10, max=10) Integer i) {
  return Math.abs(i.intValue()) >= 0;
}

If such a property is defined inside a class whose name is passed to the Unit method

  public static HashMap<String, Object[]> quickCheckClass(String name);
then your code will call absNonNeg with many different input integers ranging from -10 to 10, inclusive. For the first one for which absNonNeg returns false, quickCheckClass will add a mapping from the method name ("absNonNeg") to the array of arguments for which the method returned false or threw a Throwable. Otherwise, if the property runs until termination with only true return values, the "absNonNeg" will be mapped to null. Then quickCheckClass will run the next property in the class.

Here is the basic setup for quickCheckClass:

When running a property, your quickCheckClass method should call it will all possible combinations of arguments up to a maximum of 100 runs. The order in which you run the tests does not matter. (We will almost always be testing your code with properties for which there are fewer than 100 possible argument combinations.)

Put all your code in the code skeleton we have supplied, and upload your solution using Gradescope.