Skip to content

0.6.x manual 01.BASIC

Hiroshi Ukai edited this page Apr 6, 2016 · 4 revisions

First test with JCUnit

Below is JCUnit's most basic example 'QuadraticEquationSolver.java'. Just by running QuadraticEquationSolverTest.java as a usual JUnit test, JCUnit will automatically generate test cases based on '@FactorField' annotations.

QuadraticEquationSolver program example

To understand JCUnit's functions, let's test 'QuadraticEquationSolver.java' program, which solves 'quadratic equations' using a formula. The program contains some intentional bugs and unclear specifications (or behaviors). The formula it uses is,

    {x1, x2} = { (-b + Math.sqrt(b*b - 4*c*a)) / 2*a, (-b - Math.sqrt(b*b - 4*c*a)) / 2*a }

where {x1, x2} are the solutions of an equation,

    a * x^2 + b * x + c = 0

QuadraticEquationSolver.java (Main class, SUT)

'QuadraticEquationSolver' is the SUT (Software under test) in this example. The class provides a function to solve a quadratic equation using a quadratic formula and returns the solutions.

    //QuadraticEquationSolver.java
    public class QuadraticEquationSolver {
      private final double a;
      private final double b;
      private final double c;

      public static class Solutions {
        public final double x1;
        public final double x2;

        public Solutions(double x1, double x2) {
          this.x1 = x1;
          this.x2 = x2;
        }

        public String toString() {
          return String.format("(%f,%f)", x1, x2);
        }
      }

      public QuadraticEquationSolver(double a, double b, double c) {
        this.a = a;
        this.b = b;
        this.c = c;
      }

      public Solutions solve() {
        return new Solutions(
            (-b + Math.sqrt(b * b - 4 * c * a)) / (2 * a),
            (-b - Math.sqrt(b * b - 4 * c * a)) / (2 * a)
        );
      }
    }

Did you already notice the bugs that this program has?

  • It doesn't consider equations that do not have solutions in real.
  • If it's not a quadratic equation but a linear one, how should it behave?
  • Errors. How should it handle errors? To what extent error is acceptable?
  • Overflows. If b * b, 4 * c * a, etc become bigger than Double.MAX_VALUE (or smaller than Double.MIN_VALUE), how should it handle them?
  • Shouldn't we set some limits for a, b, and c? Both to make errors small enough and prevent overflows from happening.
  • etc. (maybe)

Try to find (and reproduce) these bugs using JCUnit and fix them.

QuadraticEquationSolverTest.java (Test)

QuadraticEquationSolverTest1 is a test class for QuadraticEquationSolver class.

    // QuadraticEquationSolverTest1.java
    @RunWith(JCUnit.class)
    public class QuadraticEquationSolverTest1 {
        @FactorField
        public int a;
        @FactorField
        public int b;
        @FactorField
        public int c;

        @Test
        public void test() {
            QuadraticEquationSolver.Solutions s = new QuadraticEquationSolver(a, b,
                    c).solve();
            assertEquals(0.0, a * s.x1 * s.x1 + b * s.x1 + c);
            assertEquals(0.0, a * s.x2 * s.x2 + b * s.x2 + c);
        }
    }

If you run this test class, JCUnit generates about fifty test cases and run them. By default, it generates the test cases by using 'all-pairs' technique.

To a primitive field annotated with FactorField, JCUnit picks up and assigns one of values defined in a default set to the field. In this example, the field a is an int and the type's default set is 1, 0, -1, 100, -100, Integer.MAX_VALUE, and Integer.MIN_VALUE(7 levels).

Since there are three factors, a, b, and c, to cover all the possible patterns, we need 343 test cases (7 * 7 * 7). But if we only mind value pairs, it is a different story. That is, for a and b, we want to cover all the possible pairs, for b and c we want to cover them, too. Also c and a.

Let's begin with a and b. Each factor has 7 levels to cover and therefore we need 49 (7 * 7) test cases to cover them. But to c, we can assign anything. Let's say we already decided levels 1 for a and 100 for b. Then we covered a tuple {a:1, b:100}. Now we are going to assign -1 to c. The test case will look like {a:1, b:100, c:-1}. This test case not only covers {a:1, b:100} but also {b:100, c:-1} and {a:1, c:-1}.

If we carefully choose values, we can make the test suite small dramatically. If we have many factors each of whose number of levels is not too big, the benefit is huge.

As of 0.6.0, JCUnit generates only 53 test cases and 39 test cases of them will fail for reasons listed in the previous chapter.

Clone this wiki locally