1*b2c653efSSorin Basca package junitparams; 2*b2c653efSSorin Basca 3*b2c653efSSorin Basca import java.util.List; 4*b2c653efSSorin Basca 5*b2c653efSSorin Basca import junitparams.internal.MethodBlockSupplier; 6*b2c653efSSorin Basca import org.junit.runner.Description; 7*b2c653efSSorin Basca import org.junit.runner.notification.RunNotifier; 8*b2c653efSSorin Basca import org.junit.runners.BlockJUnit4ClassRunner; 9*b2c653efSSorin Basca import org.junit.runners.model.FrameworkMethod; 10*b2c653efSSorin Basca import org.junit.runners.model.InitializationError; 11*b2c653efSSorin Basca import org.junit.runners.model.Statement; 12*b2c653efSSorin Basca 13*b2c653efSSorin Basca import junitparams.internal.DescribableFrameworkMethod; 14*b2c653efSSorin Basca import junitparams.internal.InstanceFrameworkMethod; 15*b2c653efSSorin Basca import junitparams.internal.InvokableFrameworkMethod; 16*b2c653efSSorin Basca import junitparams.internal.NonParameterisedFrameworkMethod; 17*b2c653efSSorin Basca import junitparams.internal.ParameterisedFrameworkMethod; 18*b2c653efSSorin Basca import junitparams.internal.TestMethod; 19*b2c653efSSorin Basca 20*b2c653efSSorin Basca /** 21*b2c653efSSorin Basca * <h1>JUnitParams</h1><br> 22*b2c653efSSorin Basca * <p> 23*b2c653efSSorin Basca * This is a JUnit runner for parameterised tests that don't suck. Annotate your test class with 24*b2c653efSSorin Basca * <code>@RunWith(JUnitParamsRunner.class)</code> and place 25*b2c653efSSorin Basca * <code>@Parameters</code> annotation on each test method which requires 26*b2c653efSSorin Basca * parameters. Nothing more needed - no special structure, no dirty tricks. 27*b2c653efSSorin Basca * </p> 28*b2c653efSSorin Basca * <br> 29*b2c653efSSorin Basca * <h2>Contents</h2> <b> <a href="#p1">1. Parameterising tests</a><br> 30*b2c653efSSorin Basca * <a href="#a">a. Parameterising tests via values 31*b2c653efSSorin Basca * in annotation</a><br> 32*b2c653efSSorin Basca * <a href="#b">b. Parameterising tests via a 33*b2c653efSSorin Basca * method that returns parameter values</a><br> 34*b2c653efSSorin Basca * <a href="#c">c. Parameterising tests via 35*b2c653efSSorin Basca * external classes</a><br> 36*b2c653efSSorin Basca * <a href="#d">d. Loading parameters from files</a><br> 37*b2c653efSSorin Basca * <a href="#d">e. Converting parameter values</a><br> 38*b2c653efSSorin Basca * <a href="#p2">2. Usage with Spring</a><br> 39*b2c653efSSorin Basca * <a href="#p3">3. Other options</a><br> 40*b2c653efSSorin Basca * </b><br> 41*b2c653efSSorin Basca * <h3 id="p1">1. Parameterising tests</h3> Parameterised tests are a great way 42*b2c653efSSorin Basca * to limit the amount of test code when you need to test the same code under 43*b2c653efSSorin Basca * different conditions. Ever tried to do it with standard JUnit tools like 44*b2c653efSSorin Basca * Parameterized runner or Theories? I always thought they're so awkward to use, 45*b2c653efSSorin Basca * that I've written this library to help all those out there who'd like to have 46*b2c653efSSorin Basca * a handy tool. 47*b2c653efSSorin Basca * 48*b2c653efSSorin Basca * So here we go. There are a few different ways to use JUnitParams, I will try 49*b2c653efSSorin Basca * to show you all of them here. 50*b2c653efSSorin Basca * 51*b2c653efSSorin Basca * <h4 id="a">a. Parameterising tests via values in annotation</h4> 52*b2c653efSSorin Basca * <p> 53*b2c653efSSorin Basca * You can parameterise your test with values defined in annotations. Just pass 54*b2c653efSSorin Basca * sets of test method argument values as an array of Strings, where each string 55*b2c653efSSorin Basca * contains the argument values separated by a comma or a pipe "|". 56*b2c653efSSorin Basca * 57*b2c653efSSorin Basca * <pre> 58*b2c653efSSorin Basca * @Test 59*b2c653efSSorin Basca * @Parameters({ "20, Tarzan", "0, Jane" }) 60*b2c653efSSorin Basca * public void cartoonCharacters(int yearsInJungle, String person) { 61*b2c653efSSorin Basca * ... 62*b2c653efSSorin Basca * } 63*b2c653efSSorin Basca * </pre> 64*b2c653efSSorin Basca * 65*b2c653efSSorin Basca * Sometimes you may be interested in passing enum values as parameters, then 66*b2c653efSSorin Basca * you can just write them as Strings like this: 67*b2c653efSSorin Basca * 68*b2c653efSSorin Basca * <pre> 69*b2c653efSSorin Basca * @Test 70*b2c653efSSorin Basca * @Parameters({ "FROM_JUNGLE", "FROM_CITY" }) 71*b2c653efSSorin Basca * public void passEnumAsParam(PersonType person) { 72*b2c653efSSorin Basca * } 73*b2c653efSSorin Basca * </pre> 74*b2c653efSSorin Basca * 75*b2c653efSSorin Basca * <h4 id="b">b. Parameterising tests via a method that returns parameter values 76*b2c653efSSorin Basca * </h4> 77*b2c653efSSorin Basca * <p> 78*b2c653efSSorin Basca * Obviously passing parameters as strings is handy only for trivial situations, 79*b2c653efSSorin Basca * that's why for normal cases you have a method that gives you a collection of 80*b2c653efSSorin Basca * parameters: 81*b2c653efSSorin Basca * 82*b2c653efSSorin Basca * <pre> 83*b2c653efSSorin Basca * @Test 84*b2c653efSSorin Basca * @Parameters(method = "cartoonCharacters") 85*b2c653efSSorin Basca * public void cartoonCharacters(int yearsInJungle, String person) { 86*b2c653efSSorin Basca * ... 87*b2c653efSSorin Basca * } 88*b2c653efSSorin Basca * private Object[] cartoonCharacters() { 89*b2c653efSSorin Basca * return $( 90*b2c653efSSorin Basca * $(0, "Tarzan"), 91*b2c653efSSorin Basca * $(20, "Jane") 92*b2c653efSSorin Basca * ); 93*b2c653efSSorin Basca * } 94*b2c653efSSorin Basca * </pre> 95*b2c653efSSorin Basca * 96*b2c653efSSorin Basca * Where <code>$(...)</code> is a static method defined in 97*b2c653efSSorin Basca * <code>JUnitParamsRunner</code> class, which returns its parameters as a 98*b2c653efSSorin Basca * <code>Object[]</code> array. Just a shortcut, so that you don't need to write the ugly <code>new Object[] {}</code> kind of stuff. 99*b2c653efSSorin Basca * 100*b2c653efSSorin Basca * <p> 101*b2c653efSSorin Basca * <code>method</code> can take more than one method name - you can pass as many 102*b2c653efSSorin Basca * of them as you want, separated by commas. This enables you to divide your 103*b2c653efSSorin Basca * test cases e.g. into categories. 104*b2c653efSSorin Basca * <pre> 105*b2c653efSSorin Basca * @Test 106*b2c653efSSorin Basca * @Parameters(method = "menCharactes, womenCharacters") 107*b2c653efSSorin Basca * public void cartoonCharacters(int yearsInJungle, String person) { 108*b2c653efSSorin Basca * ... 109*b2c653efSSorin Basca * } 110*b2c653efSSorin Basca * private Object[] menCharacters() { 111*b2c653efSSorin Basca * return $( 112*b2c653efSSorin Basca * $(20, "Tarzan"), 113*b2c653efSSorin Basca * $(2, "Chip"), 114*b2c653efSSorin Basca * $(2, "Dale") 115*b2c653efSSorin Basca * ); 116*b2c653efSSorin Basca * } 117*b2c653efSSorin Basca * private Object[] womenCharacters() { 118*b2c653efSSorin Basca * return $( 119*b2c653efSSorin Basca * $(0, "Jane"), 120*b2c653efSSorin Basca * $(18, "Pocahontas") 121*b2c653efSSorin Basca * ); 122*b2c653efSSorin Basca * } 123*b2c653efSSorin Basca * </pre> 124*b2c653efSSorin Basca * <p> 125*b2c653efSSorin Basca * The <code>method</code> argument of a <code>@Parameters</code> annotation can 126*b2c653efSSorin Basca * be ommited if the method that provides parameters has a the same name as the 127*b2c653efSSorin Basca * test, but prefixed by <code>parametersFor</code>. So our example would look 128*b2c653efSSorin Basca * like this: 129*b2c653efSSorin Basca * 130*b2c653efSSorin Basca * <pre> 131*b2c653efSSorin Basca * @Test 132*b2c653efSSorin Basca * @Parameters 133*b2c653efSSorin Basca * public void cartoonCharacters(int yearsInJungle, String person) { 134*b2c653efSSorin Basca * ... 135*b2c653efSSorin Basca * } 136*b2c653efSSorin Basca * private Object[] parametersForCartoonCharacters() { 137*b2c653efSSorin Basca * return $( 138*b2c653efSSorin Basca * $(0, "Tarzan"), 139*b2c653efSSorin Basca * $(20, "Jane") 140*b2c653efSSorin Basca * ); 141*b2c653efSSorin Basca * } 142*b2c653efSSorin Basca * </pre> 143*b2c653efSSorin Basca * 144*b2c653efSSorin Basca * <p> 145*b2c653efSSorin Basca * If you don't like returning untyped values and arrays, you can equally well 146*b2c653efSSorin Basca * return any Iterable of concrete objects: 147*b2c653efSSorin Basca * 148*b2c653efSSorin Basca * <pre> 149*b2c653efSSorin Basca * @Test 150*b2c653efSSorin Basca * @Parameters 151*b2c653efSSorin Basca * public void cartoonCharacters(Person character) { 152*b2c653efSSorin Basca * ... 153*b2c653efSSorin Basca * } 154*b2c653efSSorin Basca * private List<Person> parametersForCartoonCharacters() { 155*b2c653efSSorin Basca * return Arrays.asList( 156*b2c653efSSorin Basca * new Person(0, "Tarzan"), 157*b2c653efSSorin Basca * new Person(20, "Jane") 158*b2c653efSSorin Basca * ); 159*b2c653efSSorin Basca * } 160*b2c653efSSorin Basca * </pre> 161*b2c653efSSorin Basca * 162*b2c653efSSorin Basca * If we had more than just two Person's to make, we would get redundant, 163*b2c653efSSorin Basca * so JUnitParams gives you a simplified way of creating objects to be passed as 164*b2c653efSSorin Basca * params. You can omit the creation of the objects and just return their constructor 165*b2c653efSSorin Basca * argument values like this: 166*b2c653efSSorin Basca * 167*b2c653efSSorin Basca * <pre> 168*b2c653efSSorin Basca * @Test 169*b2c653efSSorin Basca * @Parameters 170*b2c653efSSorin Basca * public void cartoonCharacters(Person character) { 171*b2c653efSSorin Basca * ... 172*b2c653efSSorin Basca * } 173*b2c653efSSorin Basca * private List<?> parametersForCartoonCharacters() { 174*b2c653efSSorin Basca * return Arrays.asList( 175*b2c653efSSorin Basca * $(0, "Tarzan"), 176*b2c653efSSorin Basca * $(20, "Jane") 177*b2c653efSSorin Basca * ); 178*b2c653efSSorin Basca * } 179*b2c653efSSorin Basca * </pre> 180*b2c653efSSorin Basca * And JUnitParams will invoke the appropriate constructor (<code>new Person(int age, String name)</code> in this case.) 181*b2c653efSSorin Basca * <b>If you want to use it, watch out! Automatic refactoring of constructor 182*b2c653efSSorin Basca * arguments won't be working here!</b> 183*b2c653efSSorin Basca * 184*b2c653efSSorin Basca * <p> 185*b2c653efSSorin Basca * You can also define methods that provide parameters in subclasses and use 186*b2c653efSSorin Basca * them in test methods defined in superclasses, as well as redefine data 187*b2c653efSSorin Basca * providing methods in subclasses to be used by test method defined in a 188*b2c653efSSorin Basca * superclass. That you can doesn't mean you should. Inheritance in tests is 189*b2c653efSSorin Basca * usually a code smell (readability hurts), so make sure you know what you're 190*b2c653efSSorin Basca * doing. 191*b2c653efSSorin Basca * 192*b2c653efSSorin Basca * <h4 id="c">c. Parameterising tests via external classes</h4> 193*b2c653efSSorin Basca * <p> 194*b2c653efSSorin Basca * For more complex cases you may want to externalise the method that provides 195*b2c653efSSorin Basca * parameters or use more than one method to provide parameters to a single test 196*b2c653efSSorin Basca * method. You can easily do that like this: 197*b2c653efSSorin Basca * 198*b2c653efSSorin Basca * <pre> 199*b2c653efSSorin Basca * @Test 200*b2c653efSSorin Basca * @Parameters(source = CartoonCharactersProvider.class) 201*b2c653efSSorin Basca * public void testReadyToLiveInJungle(int yearsInJungle, String person) { 202*b2c653efSSorin Basca * ... 203*b2c653efSSorin Basca * } 204*b2c653efSSorin Basca * ... 205*b2c653efSSorin Basca * class CartoonCharactersProvider { 206*b2c653efSSorin Basca * public static Object[] provideCartoonCharactersManually() { 207*b2c653efSSorin Basca * return $( 208*b2c653efSSorin Basca * $(0, "Tarzan"), 209*b2c653efSSorin Basca * $(20, "Jane") 210*b2c653efSSorin Basca * ); 211*b2c653efSSorin Basca * } 212*b2c653efSSorin Basca * public static Object[] provideCartoonCharactersFromDB() { 213*b2c653efSSorin Basca * return cartoonsRepository.loadCharacters(); 214*b2c653efSSorin Basca * } 215*b2c653efSSorin Basca * } 216*b2c653efSSorin Basca * </pre> 217*b2c653efSSorin Basca * 218*b2c653efSSorin Basca * All methods starting with <code>provide</code> are used as parameter 219*b2c653efSSorin Basca * providers. 220*b2c653efSSorin Basca * 221*b2c653efSSorin Basca * <p> 222*b2c653efSSorin Basca * Sometimes though you may want to use just one or few methods of some class to 223*b2c653efSSorin Basca * provide you parameters. This can be done as well like this: 224*b2c653efSSorin Basca * 225*b2c653efSSorin Basca * <pre> 226*b2c653efSSorin Basca * @Test 227*b2c653efSSorin Basca * @Parameters(source = CartoonCharactersProvider.class, method = "cinderellaCharacters,snowwhiteCharacters") 228*b2c653efSSorin Basca * public void testPrincesses(boolean isAPrincess, String characterName) { 229*b2c653efSSorin Basca * ... 230*b2c653efSSorin Basca * } 231*b2c653efSSorin Basca * </pre> 232*b2c653efSSorin Basca * 233*b2c653efSSorin Basca * 234*b2c653efSSorin Basca * <h4 id="d">d. Loading parameters from files</h4> You may be interested in 235*b2c653efSSorin Basca * loading parameters from a file. This is very easy if it's a CSV file with 236*b2c653efSSorin Basca * columns in the same order as test method parameters: 237*b2c653efSSorin Basca * 238*b2c653efSSorin Basca * <pre> 239*b2c653efSSorin Basca * @Test 240*b2c653efSSorin Basca * @FileParameters("cartoon-characters.csv") 241*b2c653efSSorin Basca * public void shouldSurviveInJungle(int yearsInJungle, String person) { 242*b2c653efSSorin Basca * ... 243*b2c653efSSorin Basca * } 244*b2c653efSSorin Basca * </pre> 245*b2c653efSSorin Basca * 246*b2c653efSSorin Basca * But if you want to process the data from the CSV file a bit to use it in the 247*b2c653efSSorin Basca * test method arguments, you 248*b2c653efSSorin Basca * need to use an <code>IdentityMapper</code>. Look: 249*b2c653efSSorin Basca * 250*b2c653efSSorin Basca * <pre> 251*b2c653efSSorin Basca * @Test 252*b2c653efSSorin Basca * @FileParameters(value = "cartoon-characters.csv", mapper = CartoonMapper.class) 253*b2c653efSSorin Basca * public void shouldSurviveInJungle(Person person) { 254*b2c653efSSorin Basca * ... 255*b2c653efSSorin Basca * } 256*b2c653efSSorin Basca * 257*b2c653efSSorin Basca * public class CartoonMapper extends IdentityMapper { 258*b2c653efSSorin Basca * @Override 259*b2c653efSSorin Basca * public Object[] map(Reader reader) { 260*b2c653efSSorin Basca * Object[] map = super.map(reader); 261*b2c653efSSorin Basca * List<Object[]> result = new LinkedList<Object[]>(); 262*b2c653efSSorin Basca * for (Object lineObj : map) { 263*b2c653efSSorin Basca * String line = (String) lineObj; // line in a format just like in the file 264*b2c653efSSorin Basca * result.add(new Object[] { ..... }); // some format edible by the test method 265*b2c653efSSorin Basca * } 266*b2c653efSSorin Basca * return result.toArray(); 267*b2c653efSSorin Basca * } 268*b2c653efSSorin Basca * 269*b2c653efSSorin Basca * } 270*b2c653efSSorin Basca * </pre> 271*b2c653efSSorin Basca * 272*b2c653efSSorin Basca * A CSV files with a header are also supported with the use of <code>CsvWithHeaderMapper</code> class. 273*b2c653efSSorin Basca * 274*b2c653efSSorin Basca * You may also want to use a completely different file format, like excel or 275*b2c653efSSorin Basca * something. Then just parse it yourself: 276*b2c653efSSorin Basca * 277*b2c653efSSorin Basca * <pre> 278*b2c653efSSorin Basca * @Test 279*b2c653efSSorin Basca * @FileParameters(value = "cartoon-characters.xsl", mapper = ExcelCartoonMapper.class) 280*b2c653efSSorin Basca * public void shouldSurviveInJungle(Person person) { 281*b2c653efSSorin Basca * ... 282*b2c653efSSorin Basca * } 283*b2c653efSSorin Basca * 284*b2c653efSSorin Basca * public class CartoonMapper implements DataMapper { 285*b2c653efSSorin Basca * @Override 286*b2c653efSSorin Basca * public Object[] map(Reader fileReader) { 287*b2c653efSSorin Basca * ... 288*b2c653efSSorin Basca * } 289*b2c653efSSorin Basca * } 290*b2c653efSSorin Basca * </pre> 291*b2c653efSSorin Basca * 292*b2c653efSSorin Basca * As you see, you don't need to open or close the file. Just read it from the 293*b2c653efSSorin Basca * reader and parse it the way you wish. 294*b2c653efSSorin Basca * 295*b2c653efSSorin Basca * By default the file is loaded from the file system, relatively to where you start the tests from. But you can also use a resource from 296*b2c653efSSorin Basca * the classpath by prefixing the file name with <code>classpath:</code> 297*b2c653efSSorin Basca * 298*b2c653efSSorin Basca * <h4 id="e">e. Converting parameter values</h4> 299*b2c653efSSorin Basca * Sometimes you want to pass some parameter in one form, but use it in the test in another. Dates are a good example. It's handy to 300*b2c653efSSorin Basca * specify them in the parameters as a String like "2013.01.01", but you'd like to use a Jodatime's LocalDate or JDKs Date in the test 301*b2c653efSSorin Basca * without manually converting the value in the test. This is where the converters become handy. It's enough to annotate a parameter with 302*b2c653efSSorin Basca * a <code>@ConvertParam</code> annotation, give it a converter class and possibly some options (like date format in this case) and 303*b2c653efSSorin Basca * you're done. Here's an example: 304*b2c653efSSorin Basca * <pre> 305*b2c653efSSorin Basca * @Test 306*b2c653efSSorin Basca * @Parameters({ "01.12.2012, A" }) 307*b2c653efSSorin Basca * public void convertMultipleParams( 308*b2c653efSSorin Basca * @ConvertParam(value = StringToDateConverter.class, options = "dd.MM.yyyy") Date date, 309*b2c653efSSorin Basca * @ConvertParam(LetterToASCIIConverter.class) int num) { 310*b2c653efSSorin Basca * 311*b2c653efSSorin Basca * Calendar calendar = Calendar.getInstance(); 312*b2c653efSSorin Basca * calendar.setTime(date); 313*b2c653efSSorin Basca * 314*b2c653efSSorin Basca * assertEquals(2012, calendar.get(Calendar.YEAR)); 315*b2c653efSSorin Basca * assertEquals(11, calendar.get(Calendar.MONTH)); 316*b2c653efSSorin Basca * assertEquals(1, calendar.get(Calendar.DAY_OF_MONTH)); 317*b2c653efSSorin Basca * 318*b2c653efSSorin Basca * assertEquals(65, num); 319*b2c653efSSorin Basca * } 320*b2c653efSSorin Basca * </pre> 321*b2c653efSSorin Basca * 322*b2c653efSSorin Basca * <h3 id="p2">2. Usage with Spring</h3> 323*b2c653efSSorin Basca * <p> 324*b2c653efSSorin Basca * You can easily use JUnitParams together with Spring. The only problem is that 325*b2c653efSSorin Basca * Spring's test framework is based on JUnit runners, and JUnit allows only one 326*b2c653efSSorin Basca * runner to be run at once. Which would normally mean that you could use only 327*b2c653efSSorin Basca * one of Spring or JUnitParams. Luckily we can cheat Spring a little by adding 328*b2c653efSSorin Basca * this to your test class: 329*b2c653efSSorin Basca * 330*b2c653efSSorin Basca * <pre> 331*b2c653efSSorin Basca * private TestContextManager testContextManager; 332*b2c653efSSorin Basca * 333*b2c653efSSorin Basca * @Before 334*b2c653efSSorin Basca * public void init() throws Exception { 335*b2c653efSSorin Basca * this.testContextManager = new TestContextManager(getClass()); 336*b2c653efSSorin Basca * this.testContextManager.prepareTestInstance(this); 337*b2c653efSSorin Basca * } 338*b2c653efSSorin Basca * </pre> 339*b2c653efSSorin Basca * 340*b2c653efSSorin Basca * This lets you use in your tests anything that Spring provides in its test 341*b2c653efSSorin Basca * framework. 342*b2c653efSSorin Basca * 343*b2c653efSSorin Basca * <h3 id="p3">3. Other options</h3> 344*b2c653efSSorin Basca * <h4> Enhancing test case description</h4> 345*b2c653efSSorin Basca * You can use <code>TestCaseName</code> annotation to provide template of the individual test case name: 346*b2c653efSSorin Basca * <pre> 347*b2c653efSSorin Basca * @TestCaseName("factorial({0}) = {1}") 348*b2c653efSSorin Basca * @Parameters({ "1,1"}) 349*b2c653efSSorin Basca * public void fractional_test(int argument, int result) { } 350*b2c653efSSorin Basca * </pre> 351*b2c653efSSorin Basca * Will be displayed as 'fractional(1)=1' 352*b2c653efSSorin Basca * <h4>Customizing how parameter objects are shown in IDE</h4> 353*b2c653efSSorin Basca * <p> 354*b2c653efSSorin Basca * Tests show up in your IDE as a tree with test class name being the root, test 355*b2c653efSSorin Basca * methods being nodes, and parameter sets being the leaves. If you want to 356*b2c653efSSorin Basca * customize the way an parameter object is shown, create a <b>toString</b> 357*b2c653efSSorin Basca * method for it. 358*b2c653efSSorin Basca * <h4>Empty parameter sets</h4> 359*b2c653efSSorin Basca * <p> 360*b2c653efSSorin Basca * If you create a parameterised test, but won't give it any parameter sets, it 361*b2c653efSSorin Basca * will be ignored and you'll be warned about it. 362*b2c653efSSorin Basca * <h4>Parameterised test with no parameters</h4> 363*b2c653efSSorin Basca * <p> 364*b2c653efSSorin Basca * If for some reason you want to have a normal non-parameterised method to be 365*b2c653efSSorin Basca * annotated with @Parameters, then fine, you can do it. But it will be ignored 366*b2c653efSSorin Basca * then, since there won't be any params for it, and parameterised tests need 367*b2c653efSSorin Basca * parameters to execute properly (parameters are a part of test setup, right?) 368*b2c653efSSorin Basca * <h4>JUnit Rules</h4> 369*b2c653efSSorin Basca * <p> 370*b2c653efSSorin Basca * The runner for parameterised test is trying to keep all the @Rule's running, 371*b2c653efSSorin Basca * but if something doesn't work - let me know. It's pretty tricky, since the 372*b2c653efSSorin Basca * rules in JUnit are chained, but the chain is kind of... unstructured, so 373*b2c653efSSorin Basca * sometimes I need to guess how to call the next element in chain. If you have 374*b2c653efSSorin Basca * your own rule, make sure it has a field of type Statement which is the next 375*b2c653efSSorin Basca * statement in chain to call. 376*b2c653efSSorin Basca * <h4>Test inheritance</h4> 377*b2c653efSSorin Basca * <p> 378*b2c653efSSorin Basca * Although usually a bad idea, since it makes tests less readable, sometimes 379*b2c653efSSorin Basca * inheritance is the best way to remove repetitions from tests. JUnitParams is 380*b2c653efSSorin Basca * fine with inheritance - you can define a common test in the superclass, and 381*b2c653efSSorin Basca * have separate parameters provider methods in the subclasses. Also the other 382*b2c653efSSorin Basca * way around is ok, you can define parameter providers in superclass and have 383*b2c653efSSorin Basca * tests in subclasses uses them as their input. 384*b2c653efSSorin Basca * 385*b2c653efSSorin Basca * @author Pawel Lipinski ([email protected]) 386*b2c653efSSorin Basca */ 387*b2c653efSSorin Basca public class JUnitParamsRunner extends BlockJUnit4ClassRunner { 388*b2c653efSSorin Basca 389*b2c653efSSorin Basca private final MethodBlockSupplier methodBlockSupplier; 390*b2c653efSSorin Basca JUnitParamsRunner(Class<?> klass)391*b2c653efSSorin Basca public JUnitParamsRunner(Class<?> klass) throws InitializationError { 392*b2c653efSSorin Basca super(klass); 393*b2c653efSSorin Basca methodBlockSupplier = new MethodBlockSupplier() { 394*b2c653efSSorin Basca @Override 395*b2c653efSSorin Basca public Statement getMethodBlock(InvokableFrameworkMethod method) { 396*b2c653efSSorin Basca return methodBlock(method); 397*b2c653efSSorin Basca } 398*b2c653efSSorin Basca }; 399*b2c653efSSorin Basca } 400*b2c653efSSorin Basca 401*b2c653efSSorin Basca @Override collectInitializationErrors(List<Throwable> errors)402*b2c653efSSorin Basca protected void collectInitializationErrors(List<Throwable> errors) { 403*b2c653efSSorin Basca super.validateFields(errors); 404*b2c653efSSorin Basca for (Throwable throwable : errors) 405*b2c653efSSorin Basca throwable.printStackTrace(); 406*b2c653efSSorin Basca } 407*b2c653efSSorin Basca 408*b2c653efSSorin Basca @Override runChild(FrameworkMethod method, RunNotifier notifier)409*b2c653efSSorin Basca protected void runChild(FrameworkMethod method, RunNotifier notifier) { 410*b2c653efSSorin Basca DescribableFrameworkMethod describableMethod = getDescribableMethod(method); 411*b2c653efSSorin Basca if (handleIgnored(describableMethod, notifier)) 412*b2c653efSSorin Basca return; 413*b2c653efSSorin Basca 414*b2c653efSSorin Basca if (method instanceof ParameterisedFrameworkMethod) { 415*b2c653efSSorin Basca ParameterisedFrameworkMethod parameterisedFrameworkMethod = 416*b2c653efSSorin Basca (ParameterisedFrameworkMethod) method; 417*b2c653efSSorin Basca 418*b2c653efSSorin Basca List<InstanceFrameworkMethod> methods = parameterisedFrameworkMethod.getMethods(); 419*b2c653efSSorin Basca for (InstanceFrameworkMethod frameworkMethod : methods) { 420*b2c653efSSorin Basca frameworkMethod.run(methodBlockSupplier, notifier); 421*b2c653efSSorin Basca } 422*b2c653efSSorin Basca } 423*b2c653efSSorin Basca else if (describableMethod instanceof InvokableFrameworkMethod) { 424*b2c653efSSorin Basca ((InvokableFrameworkMethod) describableMethod).run(methodBlockSupplier, notifier); 425*b2c653efSSorin Basca } 426*b2c653efSSorin Basca else { 427*b2c653efSSorin Basca throw new IllegalStateException( 428*b2c653efSSorin Basca "Unsupported FrameworkMethod class: " + method.getClass()); 429*b2c653efSSorin Basca } 430*b2c653efSSorin Basca } 431*b2c653efSSorin Basca 432*b2c653efSSorin Basca /** 433*b2c653efSSorin Basca * Check that the supplied method is one that was originally in the list returned by 434*b2c653efSSorin Basca * {@link #computeTestMethods()}. 435*b2c653efSSorin Basca * 436*b2c653efSSorin Basca * @param method the method, must be an instance of {@link DescribableFrameworkMethod} 437*b2c653efSSorin Basca * @return the supplied method cast to {@link DescribableFrameworkMethod} 438*b2c653efSSorin Basca * @throws IllegalArgumentException if the supplied method is not a 439*b2c653efSSorin Basca * {@link DescribableFrameworkMethod} 440*b2c653efSSorin Basca */ getDescribableMethod(FrameworkMethod method)441*b2c653efSSorin Basca private DescribableFrameworkMethod getDescribableMethod(FrameworkMethod method) { 442*b2c653efSSorin Basca if (!(method instanceof DescribableFrameworkMethod)) { 443*b2c653efSSorin Basca throw new IllegalArgumentException( 444*b2c653efSSorin Basca "Unsupported FrameworkMethod class: " + method.getClass() 445*b2c653efSSorin Basca + ", expected a DescribableFrameworkMethod subclass"); 446*b2c653efSSorin Basca } 447*b2c653efSSorin Basca 448*b2c653efSSorin Basca return (DescribableFrameworkMethod) method; 449*b2c653efSSorin Basca } 450*b2c653efSSorin Basca handleIgnored(DescribableFrameworkMethod method, RunNotifier notifier)451*b2c653efSSorin Basca private boolean handleIgnored(DescribableFrameworkMethod method, RunNotifier notifier) { 452*b2c653efSSorin Basca // A parameterised method that is ignored (either due to @Ignore or due to empty parameters) 453*b2c653efSSorin Basca // is treated as if it was a non-parameterised method. 454*b2c653efSSorin Basca boolean ignored = (method instanceof NonParameterisedFrameworkMethod) 455*b2c653efSSorin Basca && ((NonParameterisedFrameworkMethod) method).isIgnored(); 456*b2c653efSSorin Basca if (ignored) 457*b2c653efSSorin Basca notifier.fireTestIgnored(method.getDescription()); 458*b2c653efSSorin Basca 459*b2c653efSSorin Basca return ignored; 460*b2c653efSSorin Basca } 461*b2c653efSSorin Basca 462*b2c653efSSorin Basca @Override computeTestMethods()463*b2c653efSSorin Basca protected List<FrameworkMethod> computeTestMethods() { 464*b2c653efSSorin Basca return TestMethod.listFrom(getTestClass()); 465*b2c653efSSorin Basca } 466*b2c653efSSorin Basca 467*b2c653efSSorin Basca @Override methodInvoker(FrameworkMethod method, Object test)468*b2c653efSSorin Basca protected Statement methodInvoker(FrameworkMethod method, Object test) { 469*b2c653efSSorin Basca if (method instanceof InvokableFrameworkMethod) { 470*b2c653efSSorin Basca return ((InvokableFrameworkMethod) method).getInvokeStatement(test); 471*b2c653efSSorin Basca } 472*b2c653efSSorin Basca throw new IllegalStateException( 473*b2c653efSSorin Basca "Unsupported FrameworkMethod class: " + method.getClass() 474*b2c653efSSorin Basca + ", expected an InvokableFrameworkMethod subclass"); 475*b2c653efSSorin Basca } 476*b2c653efSSorin Basca 477*b2c653efSSorin Basca @Override describeChild(FrameworkMethod method)478*b2c653efSSorin Basca protected Description describeChild(FrameworkMethod method) { 479*b2c653efSSorin Basca return getDescribableMethod(method).getDescription(); 480*b2c653efSSorin Basca } 481*b2c653efSSorin Basca 482*b2c653efSSorin Basca /** 483*b2c653efSSorin Basca * Shortcut for returning an array of objects. All parameters passed to this 484*b2c653efSSorin Basca * method are returned in an <code>Object[]</code> array. 485*b2c653efSSorin Basca * 486*b2c653efSSorin Basca * Should not be used to create var-args arrays, because of the way Java resolves 487*b2c653efSSorin Basca * var-args for objects and primitives. 488*b2c653efSSorin Basca * 489*b2c653efSSorin Basca * @deprecated This method is no longer supported. It might be removed in future 490*b2c653efSSorin Basca * as it does not support all cases (especially var-args). Create arrays using 491*b2c653efSSorin Basca * <code>new Object[]{}</code> instead. 492*b2c653efSSorin Basca * 493*b2c653efSSorin Basca * @param params 494*b2c653efSSorin Basca * Values to be returned in an <code>Object[]</code> array. 495*b2c653efSSorin Basca * @return Values passed to this method. 496*b2c653efSSorin Basca */ 497*b2c653efSSorin Basca @Deprecated $(Object... params)498*b2c653efSSorin Basca public static Object[] $(Object... params) { 499*b2c653efSSorin Basca return params; 500*b2c653efSSorin Basca } 501*b2c653efSSorin Basca } 502