| package junitparams; |
| |
| import java.util.List; |
| |
| import junitparams.internal.MethodBlockSupplier; |
| import org.junit.runner.Description; |
| import org.junit.runner.notification.RunNotifier; |
| import org.junit.runners.BlockJUnit4ClassRunner; |
| import org.junit.runners.model.FrameworkMethod; |
| import org.junit.runners.model.InitializationError; |
| import org.junit.runners.model.Statement; |
| |
| import junitparams.internal.DescribableFrameworkMethod; |
| import junitparams.internal.InstanceFrameworkMethod; |
| import junitparams.internal.InvokableFrameworkMethod; |
| import junitparams.internal.NonParameterisedFrameworkMethod; |
| import junitparams.internal.ParameterisedFrameworkMethod; |
| import junitparams.internal.TestMethod; |
| |
| /** |
| * <h1>JUnitParams</h1><br> |
| * <p> |
| * This is a JUnit runner for parameterised tests that don't suck. Annotate your test class with |
| * <code>@RunWith(JUnitParamsRunner.class)</code> and place |
| * <code>@Parameters</code> annotation on each test method which requires |
| * parameters. Nothing more needed - no special structure, no dirty tricks. |
| * </p> |
| * <br> |
| * <h2>Contents</h2> <b> <a href="#p1">1. Parameterising tests</a><br> |
| * <a href="#a">a. Parameterising tests via values |
| * in annotation</a><br> |
| * <a href="#b">b. Parameterising tests via a |
| * method that returns parameter values</a><br> |
| * <a href="#c">c. Parameterising tests via |
| * external classes</a><br> |
| * <a href="#d">d. Loading parameters from files</a><br> |
| * <a href="#d">e. Converting parameter values</a><br> |
| * <a href="#p2">2. Usage with Spring</a><br> |
| * <a href="#p3">3. Other options</a><br> |
| * </b><br> |
| * <h3 id="p1">1. Parameterising tests</h3> Parameterised tests are a great way |
| * to limit the amount of test code when you need to test the same code under |
| * different conditions. Ever tried to do it with standard JUnit tools like |
| * Parameterized runner or Theories? I always thought they're so awkward to use, |
| * that I've written this library to help all those out there who'd like to have |
| * a handy tool. |
| * |
| * So here we go. There are a few different ways to use JUnitParams, I will try |
| * to show you all of them here. |
| * |
| * <h4 id="a">a. Parameterising tests via values in annotation</h4> |
| * <p> |
| * You can parameterise your test with values defined in annotations. Just pass |
| * sets of test method argument values as an array of Strings, where each string |
| * contains the argument values separated by a comma or a pipe "|". |
| * |
| * <pre> |
| * @Test |
| * @Parameters({ "20, Tarzan", "0, Jane" }) |
| * public void cartoonCharacters(int yearsInJungle, String person) { |
| * ... |
| * } |
| * </pre> |
| * |
| * Sometimes you may be interested in passing enum values as parameters, then |
| * you can just write them as Strings like this: |
| * |
| * <pre> |
| * @Test |
| * @Parameters({ "FROM_JUNGLE", "FROM_CITY" }) |
| * public void passEnumAsParam(PersonType person) { |
| * } |
| * </pre> |
| * |
| * <h4 id="b">b. Parameterising tests via a method that returns parameter values |
| * </h4> |
| * <p> |
| * Obviously passing parameters as strings is handy only for trivial situations, |
| * that's why for normal cases you have a method that gives you a collection of |
| * parameters: |
| * |
| * <pre> |
| * @Test |
| * @Parameters(method = "cartoonCharacters") |
| * public void cartoonCharacters(int yearsInJungle, String person) { |
| * ... |
| * } |
| * private Object[] cartoonCharacters() { |
| * return $( |
| * $(0, "Tarzan"), |
| * $(20, "Jane") |
| * ); |
| * } |
| * </pre> |
| * |
| * Where <code>$(...)</code> is a static method defined in |
| * <code>JUnitParamsRunner</code> class, which returns its parameters as a |
| * <code>Object[]</code> array. Just a shortcut, so that you don't need to write the ugly <code>new Object[] {}</code> kind of stuff. |
| * |
| * <p> |
| * <code>method</code> can take more than one method name - you can pass as many |
| * of them as you want, separated by commas. This enables you to divide your |
| * test cases e.g. into categories. |
| * <pre> |
| * @Test |
| * @Parameters(method = "menCharactes, womenCharacters") |
| * public void cartoonCharacters(int yearsInJungle, String person) { |
| * ... |
| * } |
| * private Object[] menCharacters() { |
| * return $( |
| * $(20, "Tarzan"), |
| * $(2, "Chip"), |
| * $(2, "Dale") |
| * ); |
| * } |
| * private Object[] womenCharacters() { |
| * return $( |
| * $(0, "Jane"), |
| * $(18, "Pocahontas") |
| * ); |
| * } |
| * </pre> |
| * <p> |
| * The <code>method</code> argument of a <code>@Parameters</code> annotation can |
| * be ommited if the method that provides parameters has a the same name as the |
| * test, but prefixed by <code>parametersFor</code>. So our example would look |
| * like this: |
| * |
| * <pre> |
| * @Test |
| * @Parameters |
| * public void cartoonCharacters(int yearsInJungle, String person) { |
| * ... |
| * } |
| * private Object[] parametersForCartoonCharacters() { |
| * return $( |
| * $(0, "Tarzan"), |
| * $(20, "Jane") |
| * ); |
| * } |
| * </pre> |
| * |
| * <p> |
| * If you don't like returning untyped values and arrays, you can equally well |
| * return any Iterable of concrete objects: |
| * |
| * <pre> |
| * @Test |
| * @Parameters |
| * public void cartoonCharacters(Person character) { |
| * ... |
| * } |
| * private List<Person> parametersForCartoonCharacters() { |
| * return Arrays.asList( |
| * new Person(0, "Tarzan"), |
| * new Person(20, "Jane") |
| * ); |
| * } |
| * </pre> |
| * |
| * If we had more than just two Person's to make, we would get redundant, |
| * so JUnitParams gives you a simplified way of creating objects to be passed as |
| * params. You can omit the creation of the objects and just return their constructor |
| * argument values like this: |
| * |
| * <pre> |
| * @Test |
| * @Parameters |
| * public void cartoonCharacters(Person character) { |
| * ... |
| * } |
| * private List<?> parametersForCartoonCharacters() { |
| * return Arrays.asList( |
| * $(0, "Tarzan"), |
| * $(20, "Jane") |
| * ); |
| * } |
| * </pre> |
| * And JUnitParams will invoke the appropriate constructor (<code>new Person(int age, String name)</code> in this case.) |
| * <b>If you want to use it, watch out! Automatic refactoring of constructor |
| * arguments won't be working here!</b> |
| * |
| * <p> |
| * You can also define methods that provide parameters in subclasses and use |
| * them in test methods defined in superclasses, as well as redefine data |
| * providing methods in subclasses to be used by test method defined in a |
| * superclass. That you can doesn't mean you should. Inheritance in tests is |
| * usually a code smell (readability hurts), so make sure you know what you're |
| * doing. |
| * |
| * <h4 id="c">c. Parameterising tests via external classes</h4> |
| * <p> |
| * For more complex cases you may want to externalise the method that provides |
| * parameters or use more than one method to provide parameters to a single test |
| * method. You can easily do that like this: |
| * |
| * <pre> |
| * @Test |
| * @Parameters(source = CartoonCharactersProvider.class) |
| * public void testReadyToLiveInJungle(int yearsInJungle, String person) { |
| * ... |
| * } |
| * ... |
| * class CartoonCharactersProvider { |
| * public static Object[] provideCartoonCharactersManually() { |
| * return $( |
| * $(0, "Tarzan"), |
| * $(20, "Jane") |
| * ); |
| * } |
| * public static Object[] provideCartoonCharactersFromDB() { |
| * return cartoonsRepository.loadCharacters(); |
| * } |
| * } |
| * </pre> |
| * |
| * All methods starting with <code>provide</code> are used as parameter |
| * providers. |
| * |
| * <p> |
| * Sometimes though you may want to use just one or few methods of some class to |
| * provide you parameters. This can be done as well like this: |
| * |
| * <pre> |
| * @Test |
| * @Parameters(source = CartoonCharactersProvider.class, method = "cinderellaCharacters,snowwhiteCharacters") |
| * public void testPrincesses(boolean isAPrincess, String characterName) { |
| * ... |
| * } |
| * </pre> |
| * |
| * |
| * <h4 id="d">d. Loading parameters from files</h4> You may be interested in |
| * loading parameters from a file. This is very easy if it's a CSV file with |
| * columns in the same order as test method parameters: |
| * |
| * <pre> |
| * @Test |
| * @FileParameters("cartoon-characters.csv") |
| * public void shouldSurviveInJungle(int yearsInJungle, String person) { |
| * ... |
| * } |
| * </pre> |
| * |
| * But if you want to process the data from the CSV file a bit to use it in the |
| * test method arguments, you |
| * need to use an <code>IdentityMapper</code>. Look: |
| * |
| * <pre> |
| * @Test |
| * @FileParameters(value = "cartoon-characters.csv", mapper = CartoonMapper.class) |
| * public void shouldSurviveInJungle(Person person) { |
| * ... |
| * } |
| * |
| * public class CartoonMapper extends IdentityMapper { |
| * @Override |
| * public Object[] map(Reader reader) { |
| * Object[] map = super.map(reader); |
| * List<Object[]> result = new LinkedList<Object[]>(); |
| * for (Object lineObj : map) { |
| * String line = (String) lineObj; // line in a format just like in the file |
| * result.add(new Object[] { ..... }); // some format edible by the test method |
| * } |
| * return result.toArray(); |
| * } |
| * |
| * } |
| * </pre> |
| * |
| * A CSV files with a header are also supported with the use of <code>CsvWithHeaderMapper</code> class. |
| * |
| * You may also want to use a completely different file format, like excel or |
| * something. Then just parse it yourself: |
| * |
| * <pre> |
| * @Test |
| * @FileParameters(value = "cartoon-characters.xsl", mapper = ExcelCartoonMapper.class) |
| * public void shouldSurviveInJungle(Person person) { |
| * ... |
| * } |
| * |
| * public class CartoonMapper implements DataMapper { |
| * @Override |
| * public Object[] map(Reader fileReader) { |
| * ... |
| * } |
| * } |
| * </pre> |
| * |
| * As you see, you don't need to open or close the file. Just read it from the |
| * reader and parse it the way you wish. |
| * |
| * 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 |
| * the classpath by prefixing the file name with <code>classpath:</code> |
| * |
| * <h4 id="e">e. Converting parameter values</h4> |
| * 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 |
| * 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 |
| * without manually converting the value in the test. This is where the converters become handy. It's enough to annotate a parameter with |
| * a <code>@ConvertParam</code> annotation, give it a converter class and possibly some options (like date format in this case) and |
| * you're done. Here's an example: |
| * <pre> |
| * @Test |
| * @Parameters({ "01.12.2012, A" }) |
| * public void convertMultipleParams( |
| * @ConvertParam(value = StringToDateConverter.class, options = "dd.MM.yyyy") Date date, |
| * @ConvertParam(LetterToASCIIConverter.class) int num) { |
| * |
| * Calendar calendar = Calendar.getInstance(); |
| * calendar.setTime(date); |
| * |
| * assertEquals(2012, calendar.get(Calendar.YEAR)); |
| * assertEquals(11, calendar.get(Calendar.MONTH)); |
| * assertEquals(1, calendar.get(Calendar.DAY_OF_MONTH)); |
| * |
| * assertEquals(65, num); |
| * } |
| * </pre> |
| * |
| * <h3 id="p2">2. Usage with Spring</h3> |
| * <p> |
| * You can easily use JUnitParams together with Spring. The only problem is that |
| * Spring's test framework is based on JUnit runners, and JUnit allows only one |
| * runner to be run at once. Which would normally mean that you could use only |
| * one of Spring or JUnitParams. Luckily we can cheat Spring a little by adding |
| * this to your test class: |
| * |
| * <pre> |
| * private TestContextManager testContextManager; |
| * |
| * @Before |
| * public void init() throws Exception { |
| * this.testContextManager = new TestContextManager(getClass()); |
| * this.testContextManager.prepareTestInstance(this); |
| * } |
| * </pre> |
| * |
| * This lets you use in your tests anything that Spring provides in its test |
| * framework. |
| * |
| * <h3 id="p3">3. Other options</h3> |
| * <h4> Enhancing test case description</h4> |
| * You can use <code>TestCaseName</code> annotation to provide template of the individual test case name: |
| * <pre> |
| * @TestCaseName("factorial({0}) = {1}") |
| * @Parameters({ "1,1"}) |
| * public void fractional_test(int argument, int result) { } |
| * </pre> |
| * Will be displayed as 'fractional(1)=1' |
| * <h4>Customizing how parameter objects are shown in IDE</h4> |
| * <p> |
| * Tests show up in your IDE as a tree with test class name being the root, test |
| * methods being nodes, and parameter sets being the leaves. If you want to |
| * customize the way an parameter object is shown, create a <b>toString</b> |
| * method for it. |
| * <h4>Empty parameter sets</h4> |
| * <p> |
| * If you create a parameterised test, but won't give it any parameter sets, it |
| * will be ignored and you'll be warned about it. |
| * <h4>Parameterised test with no parameters</h4> |
| * <p> |
| * If for some reason you want to have a normal non-parameterised method to be |
| * annotated with @Parameters, then fine, you can do it. But it will be ignored |
| * then, since there won't be any params for it, and parameterised tests need |
| * parameters to execute properly (parameters are a part of test setup, right?) |
| * <h4>JUnit Rules</h4> |
| * <p> |
| * The runner for parameterised test is trying to keep all the @Rule's running, |
| * but if something doesn't work - let me know. It's pretty tricky, since the |
| * rules in JUnit are chained, but the chain is kind of... unstructured, so |
| * sometimes I need to guess how to call the next element in chain. If you have |
| * your own rule, make sure it has a field of type Statement which is the next |
| * statement in chain to call. |
| * <h4>Test inheritance</h4> |
| * <p> |
| * Although usually a bad idea, since it makes tests less readable, sometimes |
| * inheritance is the best way to remove repetitions from tests. JUnitParams is |
| * fine with inheritance - you can define a common test in the superclass, and |
| * have separate parameters provider methods in the subclasses. Also the other |
| * way around is ok, you can define parameter providers in superclass and have |
| * tests in subclasses uses them as their input. |
| * |
| * @author Pawel Lipinski ([email protected]) |
| */ |
| public class JUnitParamsRunner extends BlockJUnit4ClassRunner { |
| |
| private final MethodBlockSupplier methodBlockSupplier; |
| |
| public JUnitParamsRunner(Class<?> klass) throws InitializationError { |
| super(klass); |
| methodBlockSupplier = new MethodBlockSupplier() { |
| @Override |
| public Statement getMethodBlock(InvokableFrameworkMethod method) { |
| return methodBlock(method); |
| } |
| }; |
| } |
| |
| @Override |
| protected void collectInitializationErrors(List<Throwable> errors) { |
| super.validateFields(errors); |
| for (Throwable throwable : errors) |
| throwable.printStackTrace(); |
| } |
| |
| @Override |
| protected void runChild(FrameworkMethod method, RunNotifier notifier) { |
| DescribableFrameworkMethod describableMethod = getDescribableMethod(method); |
| if (handleIgnored(describableMethod, notifier)) |
| return; |
| |
| if (method instanceof ParameterisedFrameworkMethod) { |
| ParameterisedFrameworkMethod parameterisedFrameworkMethod = |
| (ParameterisedFrameworkMethod) method; |
| |
| List<InstanceFrameworkMethod> methods = parameterisedFrameworkMethod.getMethods(); |
| for (InstanceFrameworkMethod frameworkMethod : methods) { |
| frameworkMethod.run(methodBlockSupplier, notifier); |
| } |
| } |
| else if (describableMethod instanceof InvokableFrameworkMethod) { |
| ((InvokableFrameworkMethod) describableMethod).run(methodBlockSupplier, notifier); |
| } |
| else { |
| throw new IllegalStateException( |
| "Unsupported FrameworkMethod class: " + method.getClass()); |
| } |
| } |
| |
| /** |
| * Check that the supplied method is one that was originally in the list returned by |
| * {@link #computeTestMethods()}. |
| * |
| * @param method the method, must be an instance of {@link DescribableFrameworkMethod} |
| * @return the supplied method cast to {@link DescribableFrameworkMethod} |
| * @throws IllegalArgumentException if the supplied method is not a |
| * {@link DescribableFrameworkMethod} |
| */ |
| private DescribableFrameworkMethod getDescribableMethod(FrameworkMethod method) { |
| if (!(method instanceof DescribableFrameworkMethod)) { |
| throw new IllegalArgumentException( |
| "Unsupported FrameworkMethod class: " + method.getClass() |
| + ", expected a DescribableFrameworkMethod subclass"); |
| } |
| |
| return (DescribableFrameworkMethod) method; |
| } |
| |
| private boolean handleIgnored(DescribableFrameworkMethod method, RunNotifier notifier) { |
| // A parameterised method that is ignored (either due to @Ignore or due to empty parameters) |
| // is treated as if it was a non-parameterised method. |
| boolean ignored = (method instanceof NonParameterisedFrameworkMethod) |
| && ((NonParameterisedFrameworkMethod) method).isIgnored(); |
| if (ignored) |
| notifier.fireTestIgnored(method.getDescription()); |
| |
| return ignored; |
| } |
| |
| @Override |
| protected List<FrameworkMethod> computeTestMethods() { |
| return TestMethod.listFrom(getTestClass()); |
| } |
| |
| @Override |
| protected Statement methodInvoker(FrameworkMethod method, Object test) { |
| if (method instanceof InvokableFrameworkMethod) { |
| return ((InvokableFrameworkMethod) method).getInvokeStatement(test); |
| } |
| throw new IllegalStateException( |
| "Unsupported FrameworkMethod class: " + method.getClass() |
| + ", expected an InvokableFrameworkMethod subclass"); |
| } |
| |
| @Override |
| protected Description describeChild(FrameworkMethod method) { |
| return getDescribableMethod(method).getDescription(); |
| } |
| |
| /** |
| * Shortcut for returning an array of objects. All parameters passed to this |
| * method are returned in an <code>Object[]</code> array. |
| * |
| * Should not be used to create var-args arrays, because of the way Java resolves |
| * var-args for objects and primitives. |
| * |
| * @deprecated This method is no longer supported. It might be removed in future |
| * as it does not support all cases (especially var-args). Create arrays using |
| * <code>new Object[]{}</code> instead. |
| * |
| * @param params |
| * Values to be returned in an <code>Object[]</code> array. |
| * @return Values passed to this method. |
| */ |
| @Deprecated |
| public static Object[] $(Object... params) { |
| return params; |
| } |
| } |