xref: /aosp_15_r20/external/junit-params/src/main/java/junitparams/JUnitParamsRunner.java (revision b2c653efad15baf736c94ef04454e05088f2cbb7)
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>&#064;RunWith(JUnitParamsRunner.class)</code> and place
25*b2c653efSSorin Basca  * <code>&#064;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  * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#a">a. Parameterising tests via values
31*b2c653efSSorin Basca  * in annotation</a><br>
32*b2c653efSSorin Basca  * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#b">b. Parameterising tests via a
33*b2c653efSSorin Basca  * method that returns parameter values</a><br>
34*b2c653efSSorin Basca  * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#c">c. Parameterising tests via
35*b2c653efSSorin Basca  * external classes</a><br>
36*b2c653efSSorin Basca  * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#d">d. Loading parameters from files</a><br>
37*b2c653efSSorin Basca  * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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  *   &#064;Test
59*b2c653efSSorin Basca  *   &#064;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  * &#064;Test
70*b2c653efSSorin Basca  * &#064;Parameters({ &quot;FROM_JUNGLE&quot;, &quot;FROM_CITY&quot; })
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  *   &#064;Test
84*b2c653efSSorin Basca  *   &#064;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  *   &#064;Test
106*b2c653efSSorin Basca  *   &#064;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  *   &#064;Test
132*b2c653efSSorin Basca  *   &#064;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  *   &#064;Test
150*b2c653efSSorin Basca  *   &#064;Parameters
151*b2c653efSSorin Basca  *   public void cartoonCharacters(Person character) {
152*b2c653efSSorin Basca  *       ...
153*b2c653efSSorin Basca  *   }
154*b2c653efSSorin Basca  *   private List&lt;Person&gt; 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  *   &#064;Test
169*b2c653efSSorin Basca  *   &#064;Parameters
170*b2c653efSSorin Basca  *   public void cartoonCharacters(Person character) {
171*b2c653efSSorin Basca  *       ...
172*b2c653efSSorin Basca  *   }
173*b2c653efSSorin Basca  *   private List&lt;?&gt; 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  *   &#064;Test
200*b2c653efSSorin Basca  *   &#064;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  *   &#064;Test
227*b2c653efSSorin Basca  *   &#064;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  *   &#064;Test
240*b2c653efSSorin Basca  *   &#064;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  *   &#064;Test
252*b2c653efSSorin Basca  *   &#064;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  *     &#064;Override
259*b2c653efSSorin Basca  *     public Object[] map(Reader reader) {
260*b2c653efSSorin Basca  *         Object[] map = super.map(reader);
261*b2c653efSSorin Basca  *         List&lt;Object[]&gt; result = new LinkedList&lt;Object[]&gt;();
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  *   &#064;Test
279*b2c653efSSorin Basca  *   &#064;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  *     &#064;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>&#064;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  *     &#064;Test
306*b2c653efSSorin Basca  *     &#064;Parameters({ "01.12.2012, A" })
307*b2c653efSSorin Basca  *     public void convertMultipleParams(
308*b2c653efSSorin Basca  *                  &#064;ConvertParam(value = StringToDateConverter.class, options = "dd.MM.yyyy") Date date,
309*b2c653efSSorin Basca  *                  &#064;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  * &#064;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  *     &#064;TestCaseName("factorial({0}) = {1}")
348*b2c653efSSorin Basca  *     &#064;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