How to unit test with JUnit 4 (Junit 4 tutorial with examples)
In this tutorial we will look into the basics of JUnit 4 which are essential to writting quality unit tests that makes sense and are easy to maintain.
- How to use the basic annotations of JUnit4
- How to use assertions in JUnit4
- Exceptions testing in JUnit4
- JUnit4 Parameterized tests
JUnit4 - Basic annotations
Following are the most commonly used annotations and their usage in a basic unit test written in JUnit 4.
@Test
- Marks the method as a test method.@Before
and@After
sandwiches each test method in the class.@BeforeClass
and@AfterClass
sandwiches all of the test methods in a JUnit test class.
Following is the execution order of the above mentioned methods when a JUnit4 test is run.
- Method annotated with
@BeforeClass
- Method annotated with
@Before
- First method annotated with
@Test
i.e.test1()
. - Method annotated with
@After
- Method annotated with
@Before
- Second method annotated with
@Test
i.e.test2()
. - Method annotated with
@After
- Method annotated with
@AfterClass
public class SampleTest {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
//Method annotated with `@BeforeClass` will execute once before any of the test methods in this class.
//This method could be used to set up any test fixtures that are computationally expensive and shared by several test methods. e.g. establishing database connections
//Sometimes several tests need to share computationally expensive setup (like logging into a database). While this can compromise the independence of tests, sometimes it is a necessary optimization. From http://junit.sourceforge.net/javadoc/org/junit/BeforeClass.html
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
//Method annotated with `@AfterClass` will execute once after all of the test methods are executed in this class.
//If you allocate expensive external resources in a BeforeClass method you need to release them after all the tests in the class have run. Annotating a public static void method with @AfterClass causes that method to be run after all the tests in the class have been run. All @AfterClass methods are guaranteed to run even if a BeforeClass method throws an exception. From http://junit.sourceforge.net/javadoc/org/junit/AfterClass.html
}
@Before
public void setUp() throws Exception {
//Method annotated with `@Before` will execute before each test method in this class is executed.
//If you find that several tests need similar objects created before they can run this method could be used to do set up those objects (aka test-fixtures).
}
@After
public void tearDown() throws Exception {
//Method annotated with `@After` will execute after each test method in this class is executed.
//If you allocate external resources in a Before method you must release them in this method.
}
@Test
public void test1() {
//A public void method annotated with @Test will be executed as a test case.
}
@Test
public void test2() {
//Another test cases
}
}
JUnit4 - Assertions
When it comes to assertions, there is the set of old JUnit assertions such as:
- org.junit.Assert.assertArrayEquals
- org.junit.Assert.assertEquals
- org.junit.Assert.assertFalse
- org.junit.Assert.assertNotNull
- org.junit.Assert.assertNotSame
- org.junit.Assert.assertNull
- org.junit.Assert.assertSame
- org.junit.Assert.assertTrue
And the org.junit.Assert.assertThat
method (available in JUnit4) which uses matchers and is better than old style assertions because it provides:
- Better readability
assertThat(actual, is(equalTo(expected)));
is better thanassertEquals(expected, actual);
assertThat(actual, is(not(equalTo(expected))));
is better thanassertFalse(expected.equals(actual));
- Better failiure messages
-
java.lang.AssertionError: Expected: is "hello" but: was "hello world"
is better thanorg.junit.ComparisonFailure: expected:<hello[]> but was:<hello[ world]>
-
- Flexbility
-
Multiple conditions could be asserted using matchers like
anyOf
orallOf
.eg:
assertThat("hello world", anyOf(is("hello world"), containsString("hello")));
In this case, the test will pass if either the actual string is “hello world” or if it contains the word “hello”.
-
Following is a list of hamcrest coreMatchers from the hamcrest docs.
|
|
How to use assertThat
with hamcrest
matches.
@Test
public void testAssetThatExamples() {
// 'theString' should contain 'S' and 'r'
assertThat("theString", both(containsString("S")).and(containsString("r")));
List<String> items = Arrays.asList("John", "James", "Julia", "Jim");
// items list should have James and Jim
assertThat(items, hasItems("James", "Jim"));
// Every item in the list should have the character 'J'
assertThat(items, everyItem(containsString("J")));
// check all of the matchers
assertThat("Once", allOf(equalTo("Once"), startsWith("O")));
// negation of all of the matchers
assertThat("Once", not(allOf(equalTo("test"), containsString("test"))));
}
JUnit4 wiki for Assertions contains a list of examples for each of the assertions mentioned above. Also this is a comprehensive post on assertThat
. I like the table at the end the most, which is a comparison of the assertThat
with the old style assert methods, very useful.
JUnit4 - Exceptions testing
Does your method throw exceptions? There are a few different ways to verify whether expected exceptions are thrown, given the conditions. For example, we need a method which reads a file and it throws file not found exception with the message “The file ‘file_name’ does not exist!”. We can test if the file not found exception is thrown in a number of ways. The first is the simplest and the most straight forward way which is preferred, but if we need to test the exception message as well, we could make use of the other two.
Following are the three different ways to test that a method would throw the expected exception.
-
Set the
expected
parameter@Test(expected = FileNotFoundException.class)
.@Test(expected = FileNotFoundException.class) public void testReadFile() throws IOException { FileReader reader = new FileReader("test.txt"); reader.read(); reader.close(); }
-
Using
try
catch
@Test public void testReadFile2() { try { FileReader reader = new FileReader("test.txt"); reader.read(); reader.close(); fail("Expected an IOException to be thrown"); } catch (IOException e) { assertThat(e.getMessage(), is("test.txt (No such file or directory)")); } }
-
Testing with
ExpectedException
Rule.@Rule public ExpectedException thrown = ExpectedException.none(); @Test public void testReadFile3() throws IOException { thrown.expect(IOException.class); thrown.expectMessage(startsWith("test.txt (No such file or directory)")); FileReader reader = new FileReader("test.txt"); reader.read(); reader.close(); }
You could read more about exceptions testing in JUnit4 wiki for Exception testing and bad.robot - Expecting Exceptions JUnit Rule.
JUnit4 - Parameterized tests
Often times we need to test a single method with several different test data or inputs and Parameterized
tests are very useful to maintain a very clean and readable tests in such cases.
e.g. In the following example, the getTotalCharactersWithoutSpaces
method will count the number of characters ignoring any whitespace. We need to test this with different test input samples. Without Parameterized test, we would have to repeat the assertion for each of the test data which would make tests less readable and maintainable over time.
@RunWith(Parameterized.class)
public class GreetingTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ "hello world", 10 }, { "helloworld", 10 }, { "hello", 5 }
//The first item in the array is the input, and second is the expected outcome.
});
}
private String input;
private int expected;
//This constructor must be provided for the parameterized tests to work.
public GreetingTest(String input, int expected) {
this.input = input;
this.expected = expected;
}
@Test
public void test() {
Greeting greeting = new Greeting();
assertThat(greeting.getTotalCharactersWithoutSpaces(input), is(expected));
}
}
Again more elaborated examples could be found in the JUnit4 wiki for Parameterized tests
Want to see how to write JUnit tests in a Spring Boot application?
Check out the following course.
In this course you will learn how to write proper unit tests to test business logic in a Test First manner (or TDD), and also how to write seperate integration tests.