Skip to content

Using Instancio with JUnit 5

This article is an introduction to using Instancio extension for JUnit 5. Here we will cover

  • using InstancioExtension for reproducing failed tests
  • injecting settings into test classes
  • running tests with custom seed values
  • injecting generated data using @Given

Pre-requisites

  • You will need to include instancio-junit dependency.
  • It is assumed you already have JUnit 5 on the classpath.
Maven
1
2
3
4
5
6
<dependency>
    <groupId>org.instancio</groupId>
    <artifactId>instancio-junit</artifactId>
    <version>6.0.0</version>
    <scope>test</scope>
</dependency>
Gradle
1
2
3
dependencies {
    testImplementation 'org.instancio:instancio-junit:6.0.0'
}

Why use the Instancio JUnit extension

By default, Instancio tests your code against randomly generated data. Unless you configured it otherwise, each time a test is executed, it is run against a different data set. This brings up the question of how to reproduce a failed test? One of the benefits of the extension is that it reports the seed value that was used to generate the data. Knowing the seed value allows us to reproduce the original data that caused the test to fail.

To get started, we will need to declare the extension in our test class. This is similar to using other test extensions, such as MockitoExtension. In fact, they can be used together if both are needed. We will use the following sample test case verifying the conversion of a Person to PersonDTO.

Sample test class
@ExtendWith(InstancioExtension.class)
class PersonToPersonDTOTest {

    @Test
    void verifyPersonDTO() {
        Person person = Instancio.create(Person.class);

        // Method under test
        PersonDTO dto = personMapper.toDto(person);

        assertThat(dto.getFirstName()).isEqualTo(person.getFirstName());
        assertThat(dto.getLastName()).isEqualTo(person.getSurname());
        // ... remaining assertions
    }
}

If this test fails, Instancio will report the failure as follows:

Test method 'verifyPersonDTO' failed with seed: 34567

Using the reported seed value 34567, we can annotate the test method to reproduce the data:

Reproducing the data
1
2
3
4
5
6
7
8
9
@ExtendWith(InstancioExtension.class)
class ExampleTest{

    @Seed(34567)
    @Test
    void verifyPersonDTO() {
      // same code as before
    }
}

4 Placing the seed annotation as shown above will make the data effectively static.

Now each time the test is run, it will produce the same data, allowing us to fix the cause of the failure. Once the cause is resolved, the @Seed annotation can be removed so that new data will be generated on each subsequent test run. How this works is described in more detail in the user guide, but to summarise, Instancio supplies each test method with a seed value. If the @Seed annotation is present, Instancio will use its value; if not, it will generate a random seed.

Injecting Settings into tests

Another feature provided by the extension is its support for injecting custom settings. Instancio settings are encapsulated by the Settings class. This allows overriding various parameters like generated number ranges; array, map, and collection sizes; whether generated values can be null, and so on. For example, by default, Instancio generates

  • non-null values
  • non-empty collections
  • positive numbers

Using @WithSettings annotation we can override default behaviour as follows:

Injecting settings
@ExtendWith(InstancioExtension.class)
class PersonToPersonDTOTest {

    @WithSettings
    private final Settings settings = Settings.create()
            .set(Keys.COLLECTION_MIN_SIZE, 0)
            .set(Keys.COLLECTION_MAX_SIZE, 5)
            .set(Keys.INTEGER_MIN, Integer.MIN_VALUE)
            .set(Keys.INTEGER_MAX, Integer.MAX_VALUE)
            .set(Keys.STRING_NULLABLE, true);

    @Test
    void verifyPersonDTO() {
        // person will populated using above settings
        Person person = Instancio.create(Person.class);
        // ... snip
    }
}

With the above settings in place, Instancio might generate null strings, empty collections, and negative integers. The settings will apply to test methods in this test class only. If you need to override settings globally, this can be done by placing instancio.properties file at the root of the classpath.

Injecting Data with @Given

Last but not least, you can use the @Given annotation to inject generated data directly into test method parameters and fields. Instancio populates each annotated element with a fully-populated instance of its declared type.

Injecting a method parameter with @Given
1
2
3
4
5
6
7
8
@ExtendWith(InstancioExtension.class)
class PersonToPersonDTOTest {

    @Test
    void singleArgument(@Given Person person) {
        // provides a fully-populated person as an argument
    }
}

You can inject as many parameters as you need, each with its own type:

Injecting multiple parameters with @Given
1
2
3
4
@Test
void multipleArguments(@Given String str, @Given UUID uuid, @Given Foo foo) {
    // any number of arguments can be specified...
}

The @Given annotation can also be placed on fields, and works with @Test, @RepeatedTest, and @ParameterizedTest methods alike.

If you need to customise the generated data, use a custom GivenProvider, or fall back to the builder API for cases like this:

1
2
3
4
Person person = Instancio.of(Person.class)
    .set(field(Phone.class, "countryCode"), "+1")
    .set(all(LocalDateTime.class), LocalDateTime.now())
    .create();