Sometimes you may want to execute a series of tests which differ only by input values and expected results. Instead of writing each test separately, it is much better to abstract the actual tests into a single class and provide it a list of all input values and expected results. JUnit 4 introduced a standard and easy solution to this problem called parametrized tests.
Structure of a parametrized test
In order to use a parameterized test the test class must be annotated with @RunWith(Parameterized.class) annotation to inform JUnit that custom test runner should be used instead of the standard one. This custom test runner has several requirements from the test class. First, the class has to provide a static public method annotated with @Parameters annotation and returning a collection of test data elements (which in turn are stored in an array). Additionally, the test class should have a single constructor which accepts test data elements from the previously mentioned array. Typically, the constructor just stores all of its arguments into the appropriate fields of the class so they can be later accessed by test methods.
When parameterized test is executed, a new instance of a test class is created for the cross-product of each test method and each element of the collection (with test data elements). Instance of the test class is produced by passing all test data elements from an array as arguments of the constructor. Then the appropriate test method is run.
Example
Let’s consider following class with a single method:
package com.example.junitparameterizedtests; public class OneBitsCounter { int getCount(long value) { value = value - ((value >> 1) & 0x5555555555555555L); value = (value & 0x3333333333333333L) + ((value >> 2) & 0x3333333333333333L); value = ((value + (value >> 4)) & 0x0F0F0F0F0F0F0F0FL); return (int) ((value * (0x0101010101010101L)) >> 56); } }
The method is supposed to return a number of ‘1’ bits in a binary representation of the value. The code of the method is not obvious so we would like to check the method for several input values:
package com.example.junitparameterizedtests; import java.util.Arrays; import static org.junit.Assert.assertEquals; 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 OneBitsCounterTestCase { @Parameters(name = "{index}: test({0}) expected={1}") public static Iterable<Object[]> data() { return Arrays.asList(new Object[][] { { 0b0, 0}, { 0b001, 1}, { 0b11011, 4}, { 0b111111111111111111111111111, 27}, { 0b0111010111111111111111111111010101111111L, 34} }); } private long value; private int oneBitsCount; public OneBitsCounterTestCase(long value, int oneBitsCount) { this.value = value; this.oneBitsCount = oneBitsCount; } @Test public void testGetCount() { OneBitsCounter counter = new OneBitsCounter(); assertEquals(counter.getCount(value), oneBitsCount); } }
The static data() method returns five arrays containing test data elements. For each array a new instance of the test class is created using two-argument constructor. Once the object is created the actual test method is run.
Naming individual tests
Since version 4.11 of JUnit it is possible to provide an individual name for each test using a simple name pattern in @Parameters annotation. The name can contain following place-holders:
- {index} – current index of test data elements
- {0}, {1}, {2}, … – corresponding test data element
This naming can be very useful to quickly identify the failing test.
Conclusion
Support for parameterized tests is a simple yet very useful feature of JUnit enabling us to run the same test for many different sets of values. The main reason to use them is to reduce the size of source code and remove code duplication.
The complete source code of the example can be found at GitHub.
heard about https://code.google.com/p/junitparams/?
Yes. It is very good alternative to standard JUnit parameterized tests.