JUnit is one of the most popular testing frameworks out there. Version 4.11 has just been released and offers some nice improvements that you shouldn’t miss.
Dependencies
In older versions of JUnit there were two dependencies .. junit:junit contained an old version of hamcrest and could cause some nasty trouble .. junit:junit-dep just referenced hamcrest the maven way.
Now with version 4.11 there is just junit:junit with clean references to hamcrest and junit:junit-dep is relocated to junit:junit.
Maven
Adding one dependency to your pom.xml should allow you to run the following examples
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
SBT
Add the following line to your build.sbt
libraryDependencies += “junit” % “junit” % “4.11″ % “test”
Update
Robert Elliot kindly remarked that it is important to include junit:junit-dep also when using version 4.11 because there could be a dependency to junit-dep with a version ⇐4.10 in your project and this would lead to an unpredictable classloader behaviour.
So if you’re not sure if a referenced library is using or referencing junit-dep in an older version, please add the following dependency to your pom.xml
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
Or in SBT:
libraryDependencies += “junit-dep” % “junit” % “4.11″ % “test”
Parameterized Tests
It is now possible to use named parameters when running parameterized tests .. as in the following example ..
Assume we’ve got a calculator class
package com.hascode.tutorial;
public class Calculator {
public int square(final int input) {
return input * input;
}
}
We now want to run some tests with a given set of input values and expected response values .. and in addition – we want it to look nice in the test output ;)
package com.hascode.tutorial;
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ParameterizedTest {
@Parameters(name = "Run #{index}: {0}^2={1}")
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] { { 1, 1 }, { 2, 4 }, { 3, 9 },
{ 4, 16 }, { 5, 25 } });
}
private final int input;
private final int resultExpected;
public ParameterizedTest(final int input, final int result) {
this.input = input;
this.resultExpected = result;
}
@Test
public void testUserMapping() {
Calculator calc = new Calculator();
assertEquals(resultExpected, calc.square(input));
}
}
Running the test gives us a nice output like this:
Skipped Template Method for TestWatcher
TestWatcher allow us to aggregate data which tests are failing or succeeding. There is now a new method that can be overridden and that is called when an assumption in a test fails: skipped.
package com.hascode.tutorial;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import org.junit.Rule;
import org.junit.Test;
import org.junit.internal.AssumptionViolatedException;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
public class WatcherExampleTest {
@Rule
public TestWatcher watcher = new TestWatcher() {
@Override
protected void skipped(final AssumptionViolatedException e,
final Description description) {
System.out.println("method " + description.getMethodName()
+ " was skipped because of '" + e.getMessage() + "'");
}
};
@Test
public void testSomething() {
assertTrue(true);
}
@Test
public void testIgnored() {
assumeFalse(true); // assumption fails
}
}
Running this test produces the following output:
method testIgnored was skipped because of 'got: , expected: is '
Parent Folder for Temporary Folder
It is now possible to specify a parent folder for temporary folders and newFile and newFolder now fail when it’s not possible to create them
package com.hascode.tutorial;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class TemporaryFolderTest {
File parentFolder = new File("/tmp");
@Rule
public TemporaryFolder folder = new TemporaryFolder(parentFolder);
@Test
public void testUsingTempFolder() throws IOException {
File newFile = folder.newFile("testfile.txt");
File subDir = folder.newFolder("subfolder");
File someFile = new File(subDir, "test.txt");
someFile.createNewFile();
assertTrue(someFile.exists());
assertTrue(newFile.exists());
}
}
Test Execution Order
It is now possible to order the execution of test methods in a test class using a MethodSorters.
In the first example, we’re using MethodSorters.JVM that leaves the order to the random state of the virtual machine
package com.hascode.tutorial;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.JVM)
public class ExecutionOrder1Test {
@Test
public void bTest() {
System.out.println("b");
}
@Test
public void aTest() {
System.out.println("a");
}
@Test
public void dTest() {
System.out.println("d");
}
@Test
public void cTest() {
System.out.println("c");
}
}
Running this on my machine produces the following output:
b
a
d
c
Now in the second example we’re using MethodSorters.NAME_ASCENDING that executes the test methods by their name in lexicographic order
package com.hascode.tutorial;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ExecutionOrder2 {
@Test
public void bTest() {
System.out.println("b");
}
@Test
public void aTest() {
System.out.println("a");
}
@Test
public void dTest() {
System.out.println("d");
}
@Test
public void cTest() {
System.out.println("c");
}
}
Running this test produces the following output:
a
b
c
d
Other Features
-
A newer version of Hamcrest (1.3) is included
-
MethodRule is no longer deprecated
-
ExpectedException now prints the full stack trace when the exception is thrown
-
For a complete list of changes, please take a look at the JUnit 4.11 Release Notes
Tutorial Sources
Please feel free to download the tutorial sources from my GitHub repository, fork it there or clone it using Mercurial:
git clone https://github.com/hascode/junit-4.11-examples.git