TestParameterInjector ===================== [Link to Javadoc.](https://google.github.io/TestParameterInjector/docs/latest/) ## Introduction `TestParameterInjector` is a JUnit4 test runner that runs its test methods for different combinations of field/parameter values. Parameterized tests are a great way to avoid code duplication between tests and promote high test coverage for data-driven tests. There are a lot of alternative parameterized test frameworks, such as [junit.runners.Parameterized](https://github.com/junit-team/junit4/wiki/parameterized-tests) and [JUnitParams](https://github.com/Pragmatists/JUnitParams). We believe `TestParameterInjector` is an improvement of those because it is more powerful and simpler to use. [This blogpost](https://opensource.googleblog.com/2021/03/introducing-testparameterinjector.html) goes into a bit more detail about how `TestParameterInjector` compares to other frameworks used at Google. ## Getting started To start using `TestParameterInjector` right away, copy the following snippet: ```java import com.google.testing.junit.testparameterinjector.TestParameterInjector; import com.google.testing.junit.testparameterinjector.TestParameter; @RunWith(TestParameterInjector.class) public class MyTest { @TestParameter boolean isDryRun; @Test public void test1(@TestParameter boolean enableFlag) { // ... } @Test public void test2(@TestParameter MyEnum myEnum) { // ... } enum MyEnum { VALUE_A, VALUE_B, VALUE_C } } ``` And add the following dependency to your `.pom` file: ```xml com.google.testparameterinjector test-parameter-injector 1.4 ``` or see [this maven.org page](https://search.maven.org/artifact/com.google.testparameterinjector/test-parameter-injector) for instructions for other build tools. ## Basics ### `@TestParameter` for testing all combinations #### Parameterizing a single test method The simplest way to use this library is to use `@TestParameter`. For example: ```java @RunWith(TestParameterInjector.class) public class MyTest { @Test public void test(@TestParameter boolean isOwner) {...} } ``` In this example, two tests will be automatically generated by the test framework: - One with `isOwner` set to `true` - One with `isOwner` set to `false` When running the tests, the result will show the following test names: ``` MyTest#test[isOwner=true] MyTest#test[isOwner=false] ``` #### Parameterizing the whole class `@TestParameter` can also annotate a field: ```java @RunWith(TestParameterInjector.class) public class MyTest { @TestParameter private boolean isOwner; @Test public void test1() {...} @Test public void test2() {...} } ``` In this example, both `test1` and `test2` will be run twice (once for each parameter value). #### Supported types The following examples show most of the supported types. See the `@TestParameter` javadoc for more details. ```java // Enums @TestParameter AnimalEnum a; // Implies all possible values of AnimalEnum @TestParameter({"CAT", "DOG"}) AnimalEnum a; // Implies AnimalEnum.CAT and AnimalEnum.DOG. // Strings @TestParameter({"cat", "dog"}) String animalName; // Java primitives @TestParameter boolean b; // Implies {true, false} @TestParameter({"1", "2", "3"}) int i; @TestParameter({"1", "1.5", "2"}) double d; // Bytes @TestParameter({"!!binary 'ZGF0YQ=='", "some_string"}) byte[] bytes; ``` For non-primitive types (e.g. String, enums, bytes), `"null"` is always parsed as the `null` reference. #### Multiple parameters: All combinations are run If there are multiple `@TestParameter`-annotated values applicable to one test method, the test is run for all possible combinations of those values. Example: ```java @RunWith(TestParameterInjector.class) public class MyTest { @TestParameter private boolean a; @Test public void test1(@TestParameter boolean b, @TestParameter boolean c) { // Run for these combinations: // (a=false, b=false, c=false) // (a=false, b=false, c=true ) // (a=false, b=true, c=false) // (a=false, b=true, c=true ) // (a=true, b=false, c=false) // (a=true, b=false, c=true ) // (a=true, b=true, c=false) // (a=true, b=true, c=true ) } } ``` If you want to explicitly define which combinations are run, see the next sections. ### Use a test enum for enumerating more complex parameter combinations Use this strategy if you want to: - Explicitly specify the combination of parameters - or your parameters are too large to be encoded in a `String` in a readable way Example: ```java @RunWith(TestParameterInjector.class) class MyTest { enum FruitVolumeTestCase { APPLE(Fruit.newBuilder().setName("Apple").setShape(SPHERE).build(), /* expectedVolume= */ 3.1), BANANA(Fruit.newBuilder().setName("Banana").setShape(CURVED).build(), /* expectedVolume= */ 2.1), MELON(Fruit.newBuilder().setName("Melon").setShape(SPHERE).build(), /* expectedVolume= */ 6); final Fruit fruit; final double expectedVolume; FruitVolumeTestCase(Fruit fruit, double expectedVolume) { ... } } @Test public void calculateVolume_success(@TestParameter FruitVolumeTestCase fruitVolumeTestCase) { assertThat(calculateVolume(fruitVolumeTestCase.fruit)) .isEqualTo(fruitVolumeTestCase.expectedVolume); } } ``` The enum constant name has the added benefit of making for sensible test names: ``` MyTest#calculateVolume_success[APPLE] MyTest#calculateVolume_success[BANANA] MyTest#calculateVolume_success[MELON] ``` ### `@TestParameters` for defining sets of parameters You can also explicitly enumerate the sets of test parameters via a list of YAML mappings: ```java @Test @TestParameters({ "{age: 17, expectIsAdult: false}", "{age: 22, expectIsAdult: true}", }) public void personIsAdult(int age, boolean expectIsAdult) { ... } ``` The string format supports the same types as `@TestParameter` (e.g. enums). See the `@TestParameters` javadoc for more info. `@TestParameters` works in the same way on the constructor, in which case all tests will be run for the given parameter sets. ## Advanced usage ### Dynamic parameter generation for `@TestParameter` Instead of providing a list of parsable strings, you can implement your own `TestParameterValuesProvider` as follows: ```java @Test public void matchesAllOf_throwsOnNull( @TestParameter(valuesProvider = CharMatcherProvider.class) CharMatcher charMatcher) { assertThrows(NullPointerException.class, () -> charMatcher.matchesAllOf(null)); } private static final class CharMatcherProvider implements TestParameterValuesProvider { @Override public List provideValues() { return ImmutableList.of(CharMatcher.any(), CharMatcher.ascii(), CharMatcher.whitespace()); } } ``` Note that `provideValues()` dynamically construct the returned list, e.g. by reading a file. There are no restrictions on the object types returned, but note that `toString()` will be used for the test names. ### Dynamic parameter generation for `@TestParameters` Instead of providing a YAML mapping of parameters, you can implement your own `TestParametersValuesProvider` as follows: ```java @Test @TestParameters(valuesProvider = IsAdultValueProvider.class) public void personIsAdult(int age, boolean expectIsAdult) { ... } static final class IsAdultValueProvider implements TestParametersValuesProvider { @Override public ImmutableList provideValues() { return ImmutableList.of( TestParametersValues.builder() .name("teenager") .addParameter("age", 17) .addParameter("expectIsAdult", false) .build(), TestParametersValues.builder() .name("young adult") .addParameter("age", 22) .addParameter("expectIsAdult", true) .build() ); } } ```