New features in JUnit 4.11

November 18th, 2012 by

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:

Running a parameterized test.

Running a parameterized test.

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));
}
}

Tags: , , , , , ,

4 Responses to “New features in JUnit 4.11”

  1. Robert Elliot Says:

    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.

  2. micha kops Says:

    This could indeed be a problem – thanks for your remark! I’ve updated the article

  3. Eric Jablow Says:

    You really should declare test in your Maven examples. Right now, you have compile scope, which is bad.

  4. micha kops Says:

    Agreed and updated! Had set the correct scope for sbt but forgot it for the maven deps ..

Search
Categories