One of the most handy features of JUnit 4 is its ability to run parameterized tests, which essentially means you can create a generic test and run it multiple times with various test parameters. In previous versions of JUnit, if you wanted to simulate this same behavior, you’d have to either:
- loop over a collection of values
- which means if there was a failure, the loop wouldn’t terminate
- write unique test cases for each test data combination
- which could prove to be a lot of coding
With parameterized tests, you can create a highly flexible testing scenario in five easy steps:
- Create a generic test and decorate it with the @Test annotation
- Create a static feeder method that returns a Collection type and decorate it with the @Parameters annotation
- Create class members for the parameter types required in the generic method defined in Step 1
- Create a constructor that takes these parameter types and correspondingly links them to the class members defined in Step 3
- Specify the test case be run with the Parameterized class via the @RunWith annotation
For example, the following test defines a generic test dubbed verifyGoodZipCode that tests four different combinations of zip codes defined in the feeder method named regExValues. Note too, the use of the @Parameters annotation and the @RunWith(Parameterized.class) class level decoration.
@RunWith(Parameterized.class)
public class ParametricRegularExpressionTest {
private String phrase;
private boolean match;
private static String zipRegEx = "^\d{5}([\-]\d{4})?$";
private static Pattern pattern;
public ParametricRegularExpressionTest(String phrase, boolean match) {
super();
this.phrase = phrase;
this.match = match;
}
@Parameters
public static Collection regExValues() {
return Arrays.asList(new Object[][] {
{"22101", true },
{"221x1", false },
{"22101-5150", true },
{"221015150", false }});
}
@Test
public void verifyGoodZipCode() throws Exception{
Matcher mtcher = this.pattern.matcher(phrase);
boolean isValid = mtcher.matches();
assertEquals("Pattern did not validate zip code", isValid, match);
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
pattern = Pattern.compile(zipRegEx);
}
}
The astute reader will notice a subtle pattern relating to parameterized classes in JUnit 4– because of the member variable mapping with constructors, it becomes obvious that JUnit creates a new instance of the enclosing class for each test run. So in the example above, four instances will be created and the test method will be run four times with unique values relating to the Collection defined in the feeder method.
What this essentially means is that if you put a non-parametric test case in a parameterized class, it will also be run as many times as JUnit creates a new instance of your parameterized class. This isn’t a terrible thing, but it isn’t exactly elegant either. Hence, if you create parameterized test classes with JUnit 4, it probably makes sense to keep non-parameterized tests out of them. Simply put those methods in another test class.
Oh yeah, by the way, TestNG’s parameterized test classes don’t display this behavior. A non-parameterized test in a parameterized class will only be executed once. Nevertheless, JUnit 4’s parameterized test classes are an excellent way to achieve a high degree of code confidence with fewer test methods.