276 lines
7.7 KiB
Markdown
276 lines
7.7 KiB
Markdown
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
|
|
<dependency>
|
|
<groupId>com.google.testparameterinjector</groupId>
|
|
<artifactId>test-parameter-injector</artifactId>
|
|
<version>1.4</version>
|
|
</dependency>
|
|
```
|
|
|
|
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<CharMatcher> 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<TestParametersValues> 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()
|
|
);
|
|
}
|
|
}
|
|
```
|