Instancio: Data Generator For Unit Tests
Tags: testing, java
Published: 2022-06-15 21:29:01

This post is about **reducing manual data setup** in unit tests. Typically such setup involves instantiating some classes and calling a bunch of setters. For example, in the the following test this is done in the `createBookDTO()` method:

_**Note:** links to source code provided at the end._

```java
// Class under test
private final BookMapper bookMapper = new BookMapper();

@Test
void map() {
    // Given
    BookDTO bookDto = createBookDTO();

    // When
    Book book = bookMapper.toEntity(bookDto);

    // Then
assertThat(book.getTitle()).isEqualTo(bookDto.getBookTitle());
    // ... snip ... remaining assertions
}

// Data setup
private BookDTO createBookDTO() {
     AuthorDTO authorDto = new AuthorDTO();
     authorDto.setAuthorId(UUID.randomUUID().toString());
     authorDto.setFirstName("Jane");
     authorDto.setLastName("Doe");

     BookDTO bookDto = new BookDTO();
     bookDto.setBookId(UUID.randomUUID().toString());
     bookDto.setIsbn("test-isbn");
     bookDto.setBookTitle("test-title");
     bookDto.setAuthor(authorDto);
     bookDto.setGenre(Genre.FICTION);
     bookDto.setPublishedOn(LocalDate.of(1865, 3, 17));
     bookDto.setNumberOfPages(123);
     bookDto.setPriceCents(3099);
     return bookDto;
}
```

The setup done in `createBookDTO()` is very typical for unit tests. However, it can be quite tedious, especially when dealing with large classes. What's more is that often the values themselves don't actually matter. Whether you set the name to `"Jane Doe"` or `"Foo Bar"` makes no difference. The end result is the same.

If values don't matter, then why not use a library to create and populate an object with random data? This is exactly what **Instancio** does. It is a library that generates objects for unit tests. Using Instancio, we can create a `BookDTO` instance as follows:

```java
private BookDTO createBookDTO() {
    return Instancio.create(BookDTO.class);
}
```

This seems like a step in the right direction, but what if we need to customise some values of `BookDTO`? Let's say the `BookMapper` class that we are testing converts string UUID values to `UUID` type:

```java
UUID.fromString(bookId) // need a valid UUID string!
```

In this case, the test will fail unless our test objects contain valid ids (notice that `setAuthorId()` and `setBookId()` in the original `createBookDTO()` method are both set to `UUID.randomUUID().toString()`). To achieve this, we can use Instancio's builder API to set custom values:

```java
Instancio.of(BookDTO.class)
    .set(field(BookDTO.class, "bookId"), UUID.randomUUID().toString())
    .set(field(AuthorDTO.class, "authorId"), UUID.randomUUID().toString())
    .create();
```

For this simple test the above change is sufficient and now our test will pass. However, Instancio supports a number of other features for customising generated objects, for example:

```java
Instancio.of(BookDTO.class)
    .generate(field("genre"), gen -> gen.oneOf(Genre.CRIME, Genre.SCIFI, Genre.FANTASY))
    .generate(field("isbn"), gen -> gen.text().pattern("#d-#d#d#d#d#d-#d#d#d-#d"))
    .generate(field("numberOfPages"), gen -> gen.ints().range(100, 999))
    .create();
```

There might be a follow up article to go into more detail. Please leave a comment if you'd like to see more examples, or if you have any questions. All feedback is welcome!

If you are interested in learning more, please see the [user guide](https://www.instancio.org/user-guide/) which also has a number of examples.

You can find the `book-mapper-sample` project at [https://github.com/instancio/instancio-samples](https://github.com/instancio/instancio-samples)

## Links
- [https://github.com/instancio/instancio](https://github.com/instancio/instancio)
- [https://www.instancio.org](https://www.instancio.org)