1TestParameterInjector 2===================== 3 4[Link to Javadoc.](https://google.github.io/TestParameterInjector/docs/latest/) 5 6## Introduction 7 8`TestParameterInjector` is a JUnit4 test runner that runs its test methods for 9different combinations of field/parameter values. 10 11Parameterized tests are a great way to avoid code duplication between tests and 12promote high test coverage for data-driven tests. 13 14There are a lot of alternative parameterized test frameworks, such as 15[junit.runners.Parameterized](https://github.com/junit-team/junit4/wiki/parameterized-tests) 16and [JUnitParams](https://github.com/Pragmatists/JUnitParams). We believe 17`TestParameterInjector` is an improvement of those because it is more powerful 18and simpler to use. 19 20[This blogpost](https://opensource.googleblog.com/2021/03/introducing-testparameterinjector.html) 21goes into a bit more detail about how `TestParameterInjector` compares to other 22frameworks used at Google. 23 24## Getting started 25 26To start using `TestParameterInjector` right away, copy the following snippet: 27 28```java 29import com.google.testing.junit.testparameterinjector.TestParameterInjector; 30import com.google.testing.junit.testparameterinjector.TestParameter; 31 32@RunWith(TestParameterInjector.class) 33public class MyTest { 34 35 @TestParameter boolean isDryRun; 36 37 @Test public void test1(@TestParameter boolean enableFlag) { 38 // ... 39 } 40 41 @Test public void test2(@TestParameter MyEnum myEnum) { 42 // ... 43 } 44 45 enum MyEnum { VALUE_A, VALUE_B, VALUE_C } 46} 47``` 48 49And add the following dependency to your `.pom` file: 50 51```xml 52<dependency> 53 <groupId>com.google.testparameterinjector</groupId> 54 <artifactId>test-parameter-injector</artifactId> 55 <version>1.4</version> 56</dependency> 57``` 58 59or see [this maven.org 60page](https://search.maven.org/artifact/com.google.testparameterinjector/test-parameter-injector) 61for instructions for other build tools. 62 63 64## Basics 65 66### `@TestParameter` for testing all combinations 67 68#### Parameterizing a single test method 69 70The simplest way to use this library is to use `@TestParameter`. For example: 71 72```java 73@RunWith(TestParameterInjector.class) 74public class MyTest { 75 76 @Test 77 public void test(@TestParameter boolean isOwner) {...} 78} 79``` 80 81In this example, two tests will be automatically generated by the test framework: 82 83- One with `isOwner` set to `true` 84- One with `isOwner` set to `false` 85 86When running the tests, the result will show the following test names: 87 88``` 89MyTest#test[isOwner=true] 90MyTest#test[isOwner=false] 91``` 92 93#### Parameterizing the whole class 94 95`@TestParameter` can also annotate a field: 96 97```java 98@RunWith(TestParameterInjector.class) 99public class MyTest { 100 101 @TestParameter private boolean isOwner; 102 103 @Test public void test1() {...} 104 @Test public void test2() {...} 105} 106``` 107 108In this example, both `test1` and `test2` will be run twice (once for each 109parameter value). 110 111#### Supported types 112 113The following examples show most of the supported types. See the `@TestParameter` javadoc for more details. 114 115```java 116// Enums 117@TestParameter AnimalEnum a; // Implies all possible values of AnimalEnum 118@TestParameter({"CAT", "DOG"}) AnimalEnum a; // Implies AnimalEnum.CAT and AnimalEnum.DOG. 119 120// Strings 121@TestParameter({"cat", "dog"}) String animalName; 122 123// Java primitives 124@TestParameter boolean b; // Implies {true, false} 125@TestParameter({"1", "2", "3"}) int i; 126@TestParameter({"1", "1.5", "2"}) double d; 127 128// Bytes 129@TestParameter({"!!binary 'ZGF0YQ=='", "some_string"}) byte[] bytes; 130``` 131 132For non-primitive types (e.g. String, enums, bytes), `"null"` is always parsed as the `null` reference. 133 134#### Multiple parameters: All combinations are run 135 136If there are multiple `@TestParameter`-annotated values applicable to one test 137method, the test is run for all possible combinations of those values. Example: 138 139```java 140@RunWith(TestParameterInjector.class) 141public class MyTest { 142 143 @TestParameter private boolean a; 144 145 @Test public void test1(@TestParameter boolean b, @TestParameter boolean c) { 146 // Run for these combinations: 147 // (a=false, b=false, c=false) 148 // (a=false, b=false, c=true ) 149 // (a=false, b=true, c=false) 150 // (a=false, b=true, c=true ) 151 // (a=true, b=false, c=false) 152 // (a=true, b=false, c=true ) 153 // (a=true, b=true, c=false) 154 // (a=true, b=true, c=true ) 155 } 156} 157``` 158 159If you want to explicitly define which combinations are run, see the next 160sections. 161 162### Use a test enum for enumerating more complex parameter combinations 163 164Use this strategy if you want to: 165 166- Explicitly specify the combination of parameters 167- or your parameters are too large to be encoded in a `String` in a readable 168 way 169 170Example: 171 172```java 173@RunWith(TestParameterInjector.class) 174class MyTest { 175 176 enum FruitVolumeTestCase { 177 APPLE(Fruit.newBuilder().setName("Apple").setShape(SPHERE).build(), /* expectedVolume= */ 3.1), 178 BANANA(Fruit.newBuilder().setName("Banana").setShape(CURVED).build(), /* expectedVolume= */ 2.1), 179 MELON(Fruit.newBuilder().setName("Melon").setShape(SPHERE).build(), /* expectedVolume= */ 6); 180 181 final Fruit fruit; 182 final double expectedVolume; 183 184 FruitVolumeTestCase(Fruit fruit, double expectedVolume) { ... } 185 } 186 187 @Test 188 public void calculateVolume_success(@TestParameter FruitVolumeTestCase fruitVolumeTestCase) { 189 assertThat(calculateVolume(fruitVolumeTestCase.fruit)) 190 .isEqualTo(fruitVolumeTestCase.expectedVolume); 191 } 192} 193``` 194 195The enum constant name has the added benefit of making for sensible test names: 196 197``` 198MyTest#calculateVolume_success[APPLE] 199MyTest#calculateVolume_success[BANANA] 200MyTest#calculateVolume_success[MELON] 201``` 202 203### `@TestParameters` for defining sets of parameters 204 205You can also explicitly enumerate the sets of test parameters via a list of YAML 206mappings: 207 208```java 209@Test 210@TestParameters({ 211 "{age: 17, expectIsAdult: false}", 212 "{age: 22, expectIsAdult: true}", 213}) 214public void personIsAdult(int age, boolean expectIsAdult) { ... } 215``` 216 217The string format supports the same types as `@TestParameter` (e.g. enums). See 218the `@TestParameters` javadoc for more info. 219 220`@TestParameters` works in the same way on the constructor, in which case all 221tests will be run for the given parameter sets. 222 223## Advanced usage 224 225### Dynamic parameter generation for `@TestParameter` 226 227Instead of providing a list of parsable strings, you can implement your own 228`TestParameterValuesProvider` as follows: 229 230```java 231@Test 232public void matchesAllOf_throwsOnNull( 233 @TestParameter(valuesProvider = CharMatcherProvider.class) CharMatcher charMatcher) { 234 assertThrows(NullPointerException.class, () -> charMatcher.matchesAllOf(null)); 235} 236 237private static final class CharMatcherProvider implements TestParameterValuesProvider { 238 @Override 239 public List<CharMatcher> provideValues() { 240 return ImmutableList.of(CharMatcher.any(), CharMatcher.ascii(), CharMatcher.whitespace()); 241 } 242} 243``` 244 245Note that `provideValues()` dynamically construct the returned list, e.g. by 246reading a file. There are no restrictions on the object types returned, but note 247that `toString()` will be used for the test names. 248 249### Dynamic parameter generation for `@TestParameters` 250 251Instead of providing a YAML mapping of parameters, you can implement your own 252`TestParametersValuesProvider` as follows: 253 254```java 255@Test 256@TestParameters(valuesProvider = IsAdultValueProvider.class) 257public void personIsAdult(int age, boolean expectIsAdult) { ... } 258 259static final class IsAdultValueProvider implements TestParametersValuesProvider { 260 @Override public ImmutableList<TestParametersValues> provideValues() { 261 return ImmutableList.of( 262 TestParametersValues.builder() 263 .name("teenager") 264 .addParameter("age", 17) 265 .addParameter("expectIsAdult", false) 266 .build(), 267 TestParametersValues.builder() 268 .name("young adult") 269 .addParameter("age", 22) 270 .addParameter("expectIsAdult", true) 271 .build() 272 ); 273 } 274} 275``` 276