JUnit Parameterized and named tests
A relatively simple, working and complete example of JUnit Parameterized named tests.
Advantages of Parameterized in JUnit testing:
- Re-use the same test methods over and over, just changing the parameters.
- The method annotated by
@Parameterscould return a collection of data from a spreadsheet, database or hard-coded variable. - New naming mechanism lets you add a sensible label to each test to make it easy to identify the one that failed.
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public final class MultiplicationParameterizedTest {
private final int expectedResult;
private final int firstNumber;
private final int secondNumber;
public MultiplicationParameterizedTest(final int theExpectedResult,
final int theFirstNumber, final int theSecondNumber) {
expectedResult = theExpectedResult;
firstNumber = theFirstNumber;
secondNumber = theSecondNumber;
}
@Parameters(name = "Multiplication test {index}: {0}={1}x{2}")
public static Collection<Integer[]> numbersToBeMultiplied() {
return Arrays.asList(new Integer[][] {
{ 10, 5, 2 },
{ 48, 6, 8 },
{ 1452, 33, 44 },
{ 1044, 87, 12 },
{ 135, 3, 45 },
});
}
@Test
public void sum() {
final int actualResult = multiplyNumbers(firstNumber, secondNumber);
assertEquals("Expected [" + firstNumber + " * " + secondNumber + " = "
+ expectedResult + "], not [" + actualResult + "]", expectedResult,
actualResult);
}
public int multiplyNumbers(int a, int b) {
int product = a * b;
return product;
}
}
Here is what the tests look like when they pass in Eclipse. It shows you useful the naming mechanism is.
Now I change the last test case just to show what a failure looks like.
@Parameters(name = "Multiplication test {index}: {0}={1}x{2}")
public static Collection numbersToBeMultiplied() {
return Arrays.asList(new Integer[][] {
{ 10, 5, 2 },
{ 48, 6, 8 },
{ 1452, 33, 44 },
{ 1044, 87, 12 },
{ 13, 3, 45 },
});
}
And in Eclipse, that failure looks like this:
You can still use setup and tear down methods:
@Before
public void setup() {
System.out.println("Set up [" + firstNumber + " * " + secondNumber
+ " = " + expectedResult + "].");
}
@After
public void tearDown() {
System.out.println("Tear down [" + firstNumber + " * " + secondNumber
+ " = " + expectedResult + "].");
}
Note that names feature was introduced in JUnit 4.11, which is the latest stable build at the time of writing (Tuesday 21 October 2014).
What about data from a spreadsheet with lots of columns?
I am thinking of using this for tests that will have a lot of data that I want to write in a spreadsheet of ten columns or more. One problem I have with the above example is that I have a constructor accepting one parameter for each column of data. I need to encapsulate that in a class. This is because I am thinking of using this to drive Selenium tests, where I need lots of data to enter into every field in the UI. The example below shows part of how I will handle this.
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public final class MultiplicationParameterizedTestWithObject {
private final DataUnderTest data;
public MultiplicationParameterizedTestWithObject(final DataUnderTest theData) {
data = theData;
}
@Parameters(name = "Multiplication test {index}: {0}")
public static Collection<DataUnderTest[]> numbersToBeMultiplied() {
return Arrays.asList(new DataUnderTest[][] {
{ new DataUnderTest("10 = 5 * 2", 10, 5, 2) },
{ new DataUnderTest("48 = 6 * 8", 48, 6, 8) },
{ new DataUnderTest("1,452 = 33 * 44", 1452, 33, 44) },
{ new DataUnderTest("1,044 = 87 * 12", 1044, 87, 12) },
{ new DataUnderTest("135 = 3 * 45", 135, 3, 45) },
});
}
@Test
public void sum() {
final int firstNumber = data.getFirstNumber();
final int secondNumber = data.getSecondNumber();
final int actualResult = multiplyNumbers(firstNumber, secondNumber);
final int expectedResult = data.getExpectedResult();
assertEquals("Expected [" + firstNumber + " * " + secondNumber + " = "
+ expectedResult + "], not [" + actualResult + "]", expectedResult,
actualResult);
}
public int multiplyNumbers(int a, int b) {
int product = a * b;
return product;
}
private static class DataUnderTest {
private final String label;
private final int expectedResult;
private final int firstNumber;
private final int secondNumber;
public DataUnderTest(final String theLabel, final int theExpectedResult,
final int theFirstNumber, final int theSecondNumber) {
label = theLabel;
expectedResult = theExpectedResult;
firstNumber = theFirstNumber;
secondNumber = theSecondNumber;
}
@Override
public String toString() {
return label;
}
public String getLabel() {
return label;
}
public int getExpectedResult() {
return expectedResult;
}
public int getFirstNumber() {
return firstNumber;
}
public int getSecondNumber() {
return secondNumber;
}
}
}
What I will need to do differently from above in my real test cases:
DataUnderTestwill be much bigger - big enough that it will need to be in a separate java file.- The method annotated with
@Parameterswill read data from an Excel spreadsheet, perhaps using Java Excel API or The Apache POI Project.
What I like about this approach:
- JUnit Parameterized seems to scale nicely because the method annotated with
@Parameterscan get data from anywhere. - By using
DataUnderTest.toString(), I can still make good use of the same naming mechanism:@Parameters(name = "Multiplication test {index}: {0}"). By using this name declaration, the Eclipse results will look exactly the same as the first example because the {0} will be replaced byDataUnderTest.toString(). In my actual test, I plan to use the first column of data as the label.
What I don't like about this approach:
- The fact that the method annotated with
@Parametershas to return an Iterable of arrays, for exampleCollection<DataUnderTest[]>. It forces me to bind each element of the array to a constructor parameter, which is at least well defined, but doesn't seem so flexible, e.g. I cannot create an array that includesStringandintwithout making it an Object array, in which case I would lose type information and would be forced to cast or somehow convert the values back to the types I want.

