Tests

Other topics

Remarks:

ParameterContextDetails
@BeforeClassStaticExecuted when the class is first created
@BeforeInstanceExecuted before each test in the class
@TestInstanceShould be declared each method to test
@AfterInstanceExecuted after each test in the class
@AfterClassStaticExecuted before destruction of the class

Example Test Class Format

public class TestFeatureA {

    @BeforeClass
    public static void setupClass() {}
    
    @Before
    public void setupTest() {}
    
    @Test
    public void testA() {}
    
    @Test
    public void testB() {}
    
    @After
    public void tearDownTest() {}
    
    @AfterClass
    public static void tearDownClass() {}
    
    }
}

Unit testing using JUnit

Here we have a class Counter with methods countNumbers() and hasNumbers().

public class Counter {

    /* To count the numbers in the input */
    public static int countNumbers(String input) {
        int count = 0;
        for (char letter : input.toCharArray()) {
            if (Character.isDigit(letter))
                count++;
        }
        return count;
    }

    /* To check whether the input has number*/
    public static boolean hasNumber(String input) {
        return input.matches(".*\\d.*");
    }
}

To unit test this class, we can use Junit framework. Add the junit.jar in your project class path. Then create the Test case class as below:

import org.junit.Assert; // imports from the junit.jar
import org.junit.Test;

public class CounterTest {

    @Test // Test annotation makes this method as a test case
    public void countNumbersTest() {
        int expectedCount = 3;
        int actualCount = Counter.countNumbers("Hi 123");
        Assert.assertEquals(expectedCount, actualCount); //compares expected and actual value
    }

    @Test
    public void hasNumberTest() {
        boolean expectedValue = false;
        boolean actualValue = Counter.hasNumber("Hi there!");
        Assert.assertEquals(expectedValue, actualValue);
    }
}

In your IDE you can run this class as "Junit testcase" and see the output in the GUI. In command prompt you can compile and run the test case as below:

\> javac -cp ,;junit.jar CounterTest.java
\> java  -cp .;junit.jar org.junit.runner.JUnitCore CounterTest

The output from a successful test run should look similar to:

JUnit version 4.9b2
..
Time: 0.019

OK (2 tests)

In the case of a test failure it would look more like:

Time: 0.024
There was 1 failure:
1) CountNumbersTest(CounterTest)
java.lang.AssertionError: expected:<30> but was:<3>
... // truncated output
FAILURES!!!
Tests run: 2,  Failures: 1

Fixtures

From Wikipedia:

A test fixture is something used to consistently test some item, device, or piece of software.

It can also enhance readability of tests by extracting common initialisation / finalisation code from the test methods themselves.

Where common initialisation can be executed once instead of before each tests, this can also reduce the amount of time taken to run tests.

The example below is contrived to show the main options provided by JUnit. Assume a class Foo which is expensive to initialise:

public class Foo {
    public Foo() {
        // expensive initialization
    }

    public void cleanUp() {
        // cleans up resources
    }
}

Another class Bar has a reference to Foo:

public class Bar {
    private Foo foo;
    
    public Bar(Foo foo) {
        this.foo = foo;
    }

    public void cleanUp() {
        // cleans up resources
    }
}

The tests below expect an initial context of a List containing a single Bar.

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.List;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class FixturesTest {
    
    private static Foo referenceFoo;
    
    private List<Bar> testContext;
    
    @BeforeClass
    public static void setupOnce() {
        // Called once before any tests have run
        referenceFoo = new Foo();
    }

    @Before
    public void setup() {
        // Called before each test is run
        testContext = Arrays.asList(new Bar(referenceFoo));
    }
    
    @Test
    public void testSingle() {
        assertEquals("Wrong test context size", 1, testContext.size());
        Bar baz = testContext.get(0);
        assertEquals(referenceFoo, baz.getFoo());
    }
    
    @Test
    public void testMultiple() {
        testContext.add(new Bar(referenceFoo));
        assertEquals("Wrong test context size", 2, testContext.size());
        for (Bar baz : testContext) {
            assertEquals(referenceFoo, baz.getFoo());
        }
    }
    
    @After
    public void tearDown() {
        // Called after each test is run
        for (Bar baz : testContext) {
            baz.cleanUp();
        }
    }
    
    @AfterClass
    public void tearDownOnce() {
        // Called once after all tests have run
        referenceFoo.cleanUp();
    }
}

In the example the @BeforeClass annotated method setupOnce() is used to create the Foo object, which is expensive to initialise. It is important that it not be modified by any of the tests, otherwise the outcome of the test run could be dependent on the order of execution of the individual tests. The idea is that each test is independent and tests one small feature.

The @Before annotated method setup() sets up the test context. The context may be modified during test execution, which is why it must be initialised before each test. The equivalent effect could be achieved by including the code contained in this method at the start of each test method.

The @After annotated method tearDown() cleans up resources within the test context. It is called after each test invocation, and as such is often used to free resources allocated in a @Before annotated method.

The @AfterClass annotated method tearDownOnce() cleans up resources once all tests have run. Such methods are typically used to free resources allocated during initialisation or in a @BeforeClass annotated method. That said, it's probably best to avoid external resources in unit tests so that the tests don't depend on anything outside the test class.

Unit testing using Theories

From JavaDoc

The Theories runner allows to test a certain functionality against a subset of an infinite set of data points.

Running theories

import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Theories.class)
public class FixturesTest {

    @Theory
    public void theory(){
        //...some asserts
    }
}

Methods annotated with @Theory will be read as theories by Theories runner.

@DataPoint annotation

@RunWith(Theories.class)
public class FixturesTest {

    @DataPoint
    public static String dataPoint1 = "str1";
    @DataPoint
    public static String dataPoint2 = "str2";
    @DataPoint
    public static int intDataPoint = 2;

    @Theory
    public void theoryMethod(String dataPoint, int intData){
        //...some asserts
    }
}

Each field annotated with @DataPoint will be used as a method parameter of a given type in the theories. In example above theoryMethod will run two times with following parameters: ["str1", 2] , ["str2", 2]

@DataPoints annotation @RunWith(Theories.class) public class FixturesTest {

    @DataPoints
    public static String[] dataPoints = new String[]{"str1", "str2"};
    @DataPoints
    public static int[] dataPoints = new int[]{1, 2};

    @Theory
    public void theoryMethod(String dataPoint, ){
        //...some asserts
    }
}

Each element of the array annotated with @DataPoints annotation will be used as a method parameter of a given type in the theories. In example above theoryMethod will run four times with following parameters: ["str1", 1], ["str2", 1], ["str1", 2], ["str2", 2]

Performance measurement

If you need to check if your testing method takes too long to execute, you can do that by mentioning your expected execution time using timeout property of @Test annotation. If the test execution takes longer than that number of milliseconds it causes a test method to fail.

public class StringConcatenationTest {

    private static final int TIMES = 10_000;
    
    // timeout in milliseconds
    @Test(timeout = 20)
    public void testString(){

        String res = "";

        for (int i = 0; i < TIMES; i++) {
            res += i;
        }

        System.out.println(res.length());
    }

    @Test(timeout = 20)
    public void testStringBuilder(){

        StringBuilder res = new StringBuilder();

        for (int i = 0; i < TIMES; i++) {
            res.append(i);
        }

        System.out.println(res.length());
    }

    @Test(timeout = 20)
    public void testStringBuffer(){

        StringBuffer res = new StringBufferr();

        for (int i = 0; i < TIMES; i++) {
            res.append(i);
        }

        System.out.println(res.length());
    }

}

In most cases without JVM warm-up testString will fail. But testStringBuffer and testStringBuilder should pass this test successfully.

Contributors

Topic Id: 5414

Example Ids: 613,2512,9134,23168

This site is not affiliated with any of the contributors.