JUnit Testing

JUnit is a great tool that enables Java developers to easily test their code. The primary goal of unit testing is to take the smallest piece of testable software in the application, isolate it from the remainder of the code, and determine whether it behaves exactly as you expect.[1] In Java, methods are often the smallest pieces of testable software. JUnit allows you to test individual methods by defining input and making assertions about what the output should be for those inputs.


Let's dive right in with an example of some code that could use some testing:
public class StringSearch {
    /**
     * Returns the first string in the array that starts with the given character. If no String is
     * found, null is returned. The order of the strings in the array is not changed.
     *
     * @param strings     the array of Strings to search
     * @param firstLetter the first letter to search for
     * @return the first name beginning with the given letter or null otherwise
     */
    public static String findFirstOccurence(String[] strings, char firstLetter) {
        for (String string : strings) {
            if (string.charAt(0) == firstLetter) {
                return string;
            }
        }
        return null;
    }
}

Let's create a new file that will contain all of our test code for the above class. Standard practice is to name the file [Classname]Test.java to indicate which class you are testing in that file.
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

public class StringSearchTest {
    private String[] strings;

    @Before
    public void initialize() {
        strings = new String[]{"bob", "joe", "sue", "max", "jan"};
    }

    @Test
    public void testSuccessfulFind() {
        String result = StringSearch.findFirstOccurence(strings, 'j');
        assertEquals("joe", result);
    }

    @Test
    public void testUnsuccessfulFind() {
        String result = StringSearch.findFirstOccurence(strings, 'z');
        assertNull(result);
    }

    @Test
    public void testNullArray() {
        String result = StringSearch.findFirstOccurence(null, 'z');
        assertNull(result);
    }
}
JUnit is not part of the standard java library, so the import statements in lines 1-4 will not resolve without importing JUnit into your project.
If you are using Maven, you can add the following to your pom.xml file:
<dependencies>
 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
 </dependency>
</dependencies>

Otherwise, you can download JUnit from here and put the .jar file on your project's classpath.

Now that we have the import statements working, let's dissect the testing code.
    private String[] strings;

    @Before
    public void initialize() {
        strings = new String[]{"bob", "joe", "sue", "max", "jan"};
    }
This code segment initializes the input for all of the tests that we will run. The @Before annotation tells JUnit to run the method before every test. This is useful for setting up variables that will be used in every test. In this case, every test will have the same input array. By convention unless you absolutely need to, you should not make variables static. This ensures that each test is independent of all the other tests.

Additionally, you can use the @After annotation to tell JUnit to run a method after every test.

JUnit can also run methods once for the entire testing program. The @BeforeClass annotation tells JUnit to execute a method before any tests are run. The @AfterClass annotation tells JUnit to execute a method after all tests have been run. This is useful for creating and then closing network or database connections needed by the tests.


    @Test
    public void testSuccessfulFind() {
        String result = StringSearch.findFirstOccurence(strings, 'j');
        assertEquals("joe", result);
    }
The @Test annotation tells JUnit that this method represents an individual test. Line 17 gets the output from the method we are testing using the input we defined earlier. Line 18 is where the magic happens: it tells JUnit that the correct value of result should be "joe". If this assertion does not hold, the test will fail and JUnit will inform you of the failure. In this case, the assertion holds and the test passes.


    @Test
    public void testUnsuccessfulFind() {
        String result = StringSearch.findFirstOccurence(strings, 'z');
        assertNull(result);
    }
This test is very similar to the previous test but instead of asserting that result should have a specific value, we are asserting that it should be null. The Javadoc for the findFirstOccurence method states that null will be returned if no String in the array begins with the given character. Since the input array has no String beginning with 'z' this test passes.


    @Test
    public void testNullArray() {
        String result = StringSearch.findFirstOccurence(null, 'z');
        assertNull(result);
    }
The final test is almost exactly like the previous one except this time we are passing in a null array. Unfortunately, the code we wrote does not check if the input array is null and will thus throw a NullPointerException when called. As you would expect, exceptions will cause a JUnit test to fail. If we want to have this JUnit test pass we will need to modify our code to check for null arrays OR tell JUnit that we expect an exception to occur.

For the sake of learning, let's assume that we WANT a NullPointerException to be thrown if a null array is passed to the method. In order to get the JUnit test to pass, we modify the code to be:
    @Test(expected = NullPointerException.class)
    public void testNullArray() {
        String result = StringSearch.findFirstOccurence(null, 'z');
    }
The expected clause on the @Test annotation lets JUnit know that a NullPointerException should be thrown SOMEWHERE in this method. This approach is not very precise as ANY line in the test that throws a NullPointerException will cause the test to pass.

A more precise way to check for an exception is the following:
    @Rule
    public ExpectedException exception = ExpectedException.none();

    @Test
    public void testNullArray() {
        exception.expect(NullPointerException.class);
        String result = StringSearch.findFirstOccurence(null, 'z');
    }
The ExpectedException class handles more precise exception expectations by examining a single line of code in the test. In this case, the expect method called in line 31 tells JUnit that the next line (line 32) should throw a NullPointerException. Any line of code that throws an exception and is not preceded by a call to expect will cause the test to fail.

One caveat of the expect method is that it will cause the test to either pass or fail immediately. No further lines of code in the test will be executed.
    @Rule
    public ExpectedException exception = ExpectedException.none();

    @Test
    public void testNullArray() {
        exception.expect(NullPointerException.class);
        String result = StringSearch.findFirstOccurence(null, 'z');
        System.out.println("hello");
    }
This test will successfuly pass, but the "hello" in line 34 will not be printed out because the test will terminate immediately after line 33.