Tests and implementation

Figure 320. Step 1 + 2: Specify method, write skeleton Slide presentation
/**
 * Identifying prime numbers.
 */
public class Prime {
  /**
   * Check whether a given integer candidate is prime or not 
   * @param candidate A positive integer value
   * @return true if and only if candidate is a prime number.
   */
  public static boolean isPrime(int candidate) {
    return true ; //TODO: Dummy value to be implemented correctly 
  }
}

An informal specification of the method's expected behaviour. This comprises the descriptions of all method parameters among with the expected outcome.

Note that boolean isPrime(int value) is being specified as a partial method: The value parameter must not contain negative values. Thus negative values likely lead to unexpected results.

Since our current implementation is just a skeleton we simply return a constant value. Other choices:

  • return false;

  • return 231 < value;

  • ...

In fact every syntactically correct expression will do since we defer our implementation to a later step. Thus the only requirement with respect to our code is its ability to get compiled. Returning a single value obviously is the most simple way to comply.


Figure 321. Execution yet being flawed Slide presentation
Code
for (int i = 1; i < 20;i++) {
  System.out.println(i + " is " + 
      (Prime.isPrime(i) ? " a " : " not a ") + " prime number");
}
Result
1 is a prime number
2 is a prime number
3 is a prime number
4 is a prime number
5 is a prime number
...

Figure 322. Sample test data Slide presentation
Input Expected output Input Expected output
1 false 7 true
2 true 8 false
3 true 9 false
4 false 10 false
5 true 11 true
6 false 12 false

Figure 323. Step 3: Junit based specification test Slide presentation
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; 

public class PrimeTest {

  @Test  public void testIsNotPrime() {
    Assertions.assertFalse(Prime.isPrime(1));
  }
  @Test  public void testIsPrime() {
    Assertions.assertTrue(Prime.isPrime(2));
  }
  void someOrdinaryMethod()  {...}
...

A @Test annotation triggers automatic method execution by the Junit framework.

The Assertion class hosts multiple test methods e.g. assertFalse(boolean) and assertTrue(boolean).

See Assertions for details.

Any methods not being annotated by @Test will not be called by the Junit framework. They may however be called indirectly by test methods e.g. testIsNotPrime() thus acting as helper methods.


Figure 324. Junit skeleton test result (Maven CLI) Slide presentation
goik@goiki Prime_v01> mvn test
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running de.hdm_stuttgart.mi.sd1.PrimeTest
[ERROR] Tests run: 5, Failures: 3, Errors: 0, Skipped: 0, Time elapsed: 0.063 s <<< FAILURE! -- in de.hdm_stuttgart.mi.sd1.PrimeTest
[ERROR] de.hdm_stuttgart.mi.sd1.PrimeTest.testOddNonPrimes -- Time elapsed: 0.030 s <<< FAILURE!
org.opentest4j.AssertionFailedError: expected: <false> but was: <true>
        at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:158)
...

Similar to using Maven we may as well execute Junit Tests using our IDE:

Figure 325. Junit skeleton test result (IDE) Slide presentation
Junit skeleton test result (IDE)

Test test_1_isNotPrime() accidentally failing due to dummy implementation Figure 320, “Step 1 + 2: Specify method, write skeleton ”.

Test test_2_isPrime() accidentally succeeding due to dummy implementation Figure 320, “Step 1 + 2: Specify method, write skeleton ”.


Figure 326. Providing explanatory failure messages: Slide presentation
Code
@Test public void testIsNotPrime() {
    Assertions.assertFalse(Prime.isPrime(1), "1 is no prime number");
}

@Test public void testIsPrime() {
    Assertions.assertTrue(Prime.isPrime(2), "2 is a prime number");
}
Result
org.opentest4j.AssertionFailedError: 1 is no prime number ==> 
Expected :false
Actual   :true

Before replacing the skeleton implementation Figure 320, “Step 1 + 2: Specify method, write skeleton ” we supply additional tests:

Figure 327. Step 3: Providing more prime tests Slide presentation
@Test public void test_Primes() {
  Assertions.assertTrue(Prime.isPrime(3));
  Assertions.assertTrue(Prime.isPrime(5));
  Assertions.assertTrue(Prime.isPrime(7));
  Assertions.assertTrue(Prime.isPrime(11));
 ...  }
@Test public void testOddNonPrimes() {
  Assertions.assertFalse(Prime.isPrime(9));
  Assertions.assertFalse(Prime.isPrime(15));
  Assertions.assertFalse(Prime.isPrime(21));
...}

Since all even numbers greater than two are non-prime we add:

Figure 328. Step 3: Prime mass testing Slide presentation
@Test public void testEvenNonPrimes() {
  for (int i = 2; i < 100; i++) {
    Assertions.assertFalse(Prime.isPrime(2 * i));
  }
}

Now its time actually implementation isPrime():

Figure 329. Step 4: Implement skeleton Slide presentation
public static boolean isPrime(int candidate) {
  for (int i = 2; i < candidate; i++) {
    if (0 == candidate % i) { // i divides value
      return false;
    }
  }
  return candidate != 1;
}

Figure 330. Step 5: Testing our first implementation Slide presentation
goik@goiki Prime_v01> mvn test
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running de.hdm_stuttgart.mi.sd1.PrimeTest
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.047 s -- in de.hdm_stuttgart.mi.sd1.PrimeTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------