New features in JUnit 4.11
November 18th, 2012 by Micha KopsJUnit 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.
Contents
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 Bitbucket repository, fork it there or clone it using Mercurial:
hg clone https://bitbucket.org/hascode/junit-4.11-examples
Resources
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));
}
}
November 20th, 2012 at 1:54 pm
I’d suggest adding the following to your pom as well:
<dependency>
<artifactId>junit-dep</artifactId>
<groupId>junit</groupId>
<version>4.11</version>
</dependency>
Otherwise if you depend on a library which in turn depends on junit-dep 4.10 or earlier that version of junit-dep will get on to the classpath, resulting in random class loading behaviour.
November 20th, 2012 at 5:29 pm
This could indeed be a problem – thanks for your remark! I’ve updated the article
November 21st, 2012 at 6:15 am
You really should declare test in your Maven examples. Right now, you have compile scope, which is bad.
November 21st, 2012 at 8:53 am
Agreed and updated! Had set the correct scope for sbt but forgot it for the maven deps ..