Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/google/TestParameterInjector
A simple yet powerful parameterized test runner for Java.
https://github.com/google/TestParameterInjector
java junit4 junit5 parameterized-tests testing
Last synced: about 2 months ago
JSON representation
A simple yet powerful parameterized test runner for Java.
- Host: GitHub
- URL: https://github.com/google/TestParameterInjector
- Owner: google
- License: apache-2.0
- Created: 2021-02-16T21:34:24.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2024-08-29T11:25:55.000Z (4 months ago)
- Last Synced: 2024-08-30T11:28:50.305Z (4 months ago)
- Topics: java, junit4, junit5, parameterized-tests, testing
- Language: Java
- Homepage:
- Size: 2.17 MB
- Stars: 391
- Watchers: 6
- Forks: 33
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
- awesome-list - google/TestParameterInjector - A simple yet powerful parameterized test runner for Java. (Java)
README
TestParameterInjector
=====================[Link to Javadoc.](https://google.github.io/TestParameterInjector/docs/latest/)
## Introduction
`TestParameterInjector` is a JUnit4 and JUnit5 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
### JUnit4
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.18
test```
or see [this maven.org
page](https://search.maven.org/artifact/com.google.testparameterinjector/test-parameter-injector)
for instructions for other build tools.### JUnit5 (Jupiter)
Click to expand
To start using `TestParameterInjector` right away, copy the following snippet:
```java
import com.google.testing.junit.testparameterinjector.junit5.TestParameterInjectorTest;
import com.google.testing.junit.testparameterinjector.junit5.TestParameter;class MyTest {
@TestParameter boolean isDryRun;
@TestParameterInjectorTest
void test1(@TestParameter boolean enableFlag) {
// ...
}@TestParameterInjectorTest
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-junit5
1.18
test```
or see [this maven.org
page](https://search.maven.org/artifact/com.google.testparameterinjector/test-parameter-injector-junit5)
for instructions for other build tools.## Basics
**Note about JUnit4 vs JUnit5:**
The code below assumes you're using JUnit4. For JUnit5 users, simply remove the
`@RunWith` annotation and replace `@Test` by `@TestParameterInjectorTest`.### `@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).The test runner will set these fields before calling any methods, so it is safe
to use such `@TestParameter`-annotated fields for setting up other test values
and behavior in `@Before` methods.#### 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;// Durations (segments of number+unit as shown below)
@TestParameter({"1d", "2h", "3min", "4s", "5ms", "6us", "7ns"}) java.time.Duration d;
@TestParameter({"1h30min", "-2h10min20s", "1.5h", ".5s", "0"}) java.time.Duration d;
```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
wayExample:
```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}")
@TestParameters("{age: 22, expectIsAdult: true}")
public void personIsAdult(int age, boolean expectIsAdult) { ... }
```which would generate the following tests:
```
MyTest#personIsAdult[{age: 17, expectIsAdult: false}]
MyTest#personIsAdult[{age: 22, expectIsAdult: true}]
```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.> Tip: Consider setting a custom name if the YAML string is large:
>
> ```java
> @Test
> @TestParameters(customName = "teenager", value = "{age: 17, expectIsAdult: false}")
> @TestParameters(customName = "young adult", value = "{age: 22, expectIsAdult: true}")
> public void personIsAdult(int age, boolean expectIsAdult) { ... }
> ```
>
> This will generate the following test names:
>
> ```
> MyTest#personIsAdult[teenager]
> MyTest#personIsAdult[young adult]
> ```### Filtering unwanted parameters
Sometimes, you want to exclude a parameter or a combination of parameters. We
recommend doing this via JUnit assumptions which is also supported by
[Truth](https://truth.dev/):```java
import static com.google.common.truth.TruthJUnit.assume;@Test
public void myTest(@TestParameter Fruit fruit) {
assume().that(fruit).isNotEqualTo(Fruit.BANANA);// At this point, the test will only run for APPLE and CHERRY.
// The BANANA case will silently be ignored.
}enum Fruit { APPLE, BANANA, CHERRY }
```Note that the above works regardless of what parameterization framework you
choose.## Advanced usage
**Note about JUnit4 vs JUnit5:**
The code below assumes you're using JUnit4. For JUnit5 users, simply remove the
`@RunWith` annotation and replace `@Test` by `@TestParameterInjectorTest`.### Dynamic parameter generation for `@TestParameter`
Instead of providing a list of parsable strings, you can implement your own
`TestParameterValuesProvider` as follows:```java
import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider;@Test
public void matchesAllOf_throwsOnNull(
@TestParameter(valuesProvider = CharMatcherProvider.class) CharMatcher charMatcher) {
assertThrows(NullPointerException.class, () -> charMatcher.matchesAllOf(null));
}private static final class CharMatcherProvider extends TestParameterValuesProvider {
@Override
public List provideValues(Context context) {
return ImmutableList.of(CharMatcher.any(), CharMatcher.ascii(), CharMatcher.whitespace());
}
}
```Notes:
- The `provideValues()` method can dynamically construct the returned list,
e.g. by reading a file.
- There are no restrictions on the object types returned.
- The `provideValues()` method is called before `@BeforeClass`, so don't rely
on any static state initialized in there.
- The returned objects' `toString()` will be used for the test names. If you
want to customize the value names, you can do that as follows:```
private static final class FruitProvider extends TestParameterValuesProvider {
@Override
public List> provideValues(Context context) {
return ImmutableList.of(
value(new Apple()).withName("apple"),
value(new Banana()).withName("banana"));
}
}
```- The given `Context` contains the test class and other annotations on the
`@TestParameter`-annotated parameter/field. This allows more generic
providers that take into account custom annotations with extra data, or the
implementation of abstract methods on a base test class.### Dynamic parameter generation for `@TestParameters`
Instead of providing a YAML mapping of parameters, you can implement your own
`TestParametersValuesProvider` as follows:```java
import com.google.testing.junit.testparameterinjector.TestParametersValuesProvider;
import com.google.testing.junit.testparameterinjector.TestParameters.TestParametersValues;@Test
@TestParameters(valuesProvider = IsAdultValueProvider.class)
public void personIsAdult(int age, boolean expectIsAdult) { ... }static final class IsAdultValueProvider extends TestParametersValuesProvider {
@Override public ImmutableList provideValues(Context context) {
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()
);
}
}
```