What if you could generate sensible, random, yet controllable test data with minimal effort?
Well, this can be done easily, so here is an open invitation to Enter the world of Instancio!

At N47, we apply practices like this across our software engineering engagements to keep test suites fast and maintainable. Developing robust software requires comprehensive testing. But let’s be honest, creating realistic, varied test data for your Java objects can be a tedious, error-prone, and time-consuming task. For most unit tests, the actual values rarely matter. What matters is that something is there. With that in mind, Instancio focuses on one thing—quickly generating fully populated objects filled with random data. That includes arrays, collections, nested structures, generic types, and more. The goal is to make test setup effortless, so your tests stay short, readable, and focused on what they’re really meant to verify.

Why Choose Instancio?

  • Saves Time: Drastically reduces the time spent on creating test data.
  • Reduces Boilerplate: No more lengthy new MyObject().setX(val).setY(val)... chains.
  • Increases Test Coverage: Easily generate diverse data, including nulls, empty collections, and various valid ranges, helping uncover edge cases.
  • Type-Safe Customization: Use method references (MyClass::getField) for compile-time safety when customizing fields.
  • Highly Extensible: Provides a rich API for fine-grained control over data generation.
  • Readable Tests: Your tests become cleaner and more focused on behavior.

Getting Started: Add Instancio to Your Project

First, you’ll need to add Instancio to your pom.xml (for this example, Maven is being used). Just one note: Java 17+ is required.

<properties>
    <instancio.version>5.5.1</instancio.version>
    <junit.version>6.0.3</junit.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.instancio</groupId>
        <artifactId>instancio-junit</artifactId>
        <version>${instancio.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Setup The Domain: Person, News Agency, and Article

Let’s define a simple domain model to demonstrate Instancio’s capabilities. We’ll imagine a NewsAgency that employs Person objects as staff and publishes Article objects.

public class Person {
    private String name;
    private int age;
    private String email;
    private LocalDate dateOfBirth;
    private Address address;
}

public class Article {
    private String title;
    private String content;
    private Person author;
    private LocalDateTime publicationDate;
    private Set<String> tags;
}

public class NewsAgency {
    private String name;
    private List<Article> articles;
    private Set<Person> staff;
}

Instancio in Action: Most Useful Features with Examples

Let’s explore Instancio’s core features using our domain models.

1. Basic Object Generation

The simplest way to use Instancio is to just create an instance of a class. Instancio will traverse the object graph and populate all fields with sensible random data.

@ExtendWith(InstancioExtension.class)
class InstancioExamplesTest {

    @Test
    void testBasicPersonGeneration() {
        Person person = Instancio.create(Person.class);
        System.out.println("Generated Person: " + person);
        assertNotNull(person);
        assertNotNull(person.getName());
        assertTrue(person.getAge() > 0);
        assertNotNull(person.getEmail());
        assertNotNull(person.getAddress());
        assertNotNull(person.getAddress().getCity());
    }
}

Note that, if you are combining Instancio with Mockito, you still need to add @ExtendWith(MockitoExtension.class). Same goes, if there are cases where specific features of Spring test context is needed. For instance, @WebMvcTest or @SpringBootTest. In that case you need to include the @ExtendWith(SpringExtension.class). But for regular usage, @ExtendWith(InstancioExtension.class) should be sufficient.

2. Targeted Customization with Selectors

Instancio’s power comes from its Select API, allowing you to target specific fields, classes, or even the entire object graph for customization.

    @Test
    void testCustomizingSpecificFields() {
        Person youngAdult = Instancio.of(Person.class)
                .set(field(Person::getAge), 25)
                .set(field(Person::getEmail), "john.doe@example.com")
                .set(field(Person::getAddress).andThen(field("city")), "Skopje")
                .create();
        System.out.println("Customized Person: " + youngAdult);
        assertEquals(25, youngAdult.getAge());
        assertEquals("john.doe@example.com", youngAdult.getEmail());
        assertEquals("Skopje", youngAdult.getAddress().getCity());
    }

    @Test
    void testCustomizingAllFieldsOfType() {
        Article article = Instancio.of(Article.class)
                .set(all(String.class), "DEFAULT_TEXT")
                .set(field(Article::getTitle), "Enter the world of Instancio")
                .create();
        System.out.println("N47 blogpost with all Strings customized: " + article);
        assertEquals("Enter the world of Instancio", article.getTitle());
        assertEquals("DEFAULT_TEXT", article.getContent());
        assertEquals("DEFAULT_TEXT", article.getAuthor().getName());
    }

3. Leveraging Generators

Instancio provides a rich set of built-in generators (Gen) for common data types (numbers, dates, strings, enums, collections, etc.). You can also define custom generators.

    @Test
    void testUsingBuiltInGenerators() {
    Article article = Instancio.of(Article.class)
            .generate(field(Article::getPublicationDate), gen -> gen.localDateTime().past())
            .generate(field(Article::getContent), gen -> gen.strings().maxLength(100))          

    .generate(field(Article::getAuthor).andThen(field(Person::getAge)),        gen -> gen.ints().range(20, 60))
                .create();
       System.out.println("Article with generated data: " + article);

                 assertTrue(article.getPublicationDate().isBefore(LocalDateTime.now()));
        assertTrue(article.getContent().length() <= 100);
        assertTrue(article.getAuthor().getAge() >= 20 && article.getAuthor().getAge() <= 60);
    }

4. Handling Collections and Arrays

Easily control the size of collections and arrays.

    @Test
    void testCollectionAndArraySizes() {
        NewsAgency agency = Instancio.of(NewsAgency.class)
                .withCollectionSize(field(NewsAgency::getArticles), 5) 
                .withCollectionSize(field(NewsAgency::getStaff), 3)    
                .withCollectionSize(field(Article::getTags), 2)        
                .create();

        System.out.println("News Agency with specific collection sizes: " + agency);

        assertEquals(5, agency.getArticles().size());
        assertEquals(3, agency.getStaff().size());
        agency.getArticles().forEach(article -> assertEquals(2, article.getTags().size()));
    }

5. Ignoring Fields

Sometimes you want certain fields to remain null or uninitialized by Instancio.

  @Test
  void testIgnoringFields() {
        Article draftArticle = Instancio.of(Article.class)
                .ignore(field(Article::getPublicationDate))
  .ignore(field(Article::getAuthor).andThen(field(Person::getAddress)))
                .create();

        System.out.println("Draft Article with ignored fields: " + draftArticle);

        assertNull(draftArticle.getPublicationDate());
        assertNull(draftArticle.getAuthor().getAddress());
        assertNotNull(draftArticle.getAuthor().getName());
    }

6. Making Fields Nullable

By default, Instancio tries to generate non-null values. Use withNullable() to introduce randomness in nullability.

    @Test
    void testMakingFieldsNullable() {
        Person potentialNullsPerson = Instancio.of(Person.class)
                .withNullable(field(Person::getEmail))
                .withNullable(field(Person::getAddress).andThen(field(Address::getPostcode)))
                .create();

        System.out.println("Person with potential nulls: " + potentialNullsPerson);
    }

7. Reproducible Data with Seeding

For deterministic tests, you can seed Instancio’s random number generator. The same seed will always produce the same generated data.

    @Test
    void testReproducibleDataWithSeed() {
        Person person1 = Instancio.of(Person.class)
                .withSeed(123L)
                .create();

        Person person2 = Instancio.of(Person.class)
                .withSeed(123L)
                .create();

        System.out.println("Person 1 (seed 123): " + person1);
        System.out.println("Person 2 (seed 123): " + person2);

        assertEquals(person1.getName(), person2.getName());
        assertEquals(person1.getAge(), person2.getAge());
    }

8. Complex Object Graph Generation for JUnit

Let’s put it all together to generate a complex NewsAgency object and use it in a JUnit test.

    @Test
    void testNewsAgencyGenerationAndAssertions() {
        NewsAgency newsAgency = Instancio.of(NewsAgency.class)
                .set(field(NewsAgency::getName), "Sample News Agency")
                .withCollectionSize(field(NewsAgency::getStaff), 5)
                .withCollectionSize(field(NewsAgency::getArticles), 10)
                .generate(field(Person::getAge), gen -> gen.ints().range(26, 65))
                .generate(field(Article::getPublicationDate), gen -> gen.localDateTime().past(1, java.time.temporal.ChronoUnit.MONTHS))
                .generate(field(Article::getContent), gen -> gen.strings().length(200, 500))
                .withCollectionSize(field(Article::getTags), 3, 5)
                .create();

        System.out.println("\n--- Generated News Agency for Testing ---");
        System.out.println("Agency Name: " + newsAgency.getName());
        System.out.println("Number of Staff: " + newsAgency.getStaff().size());
        System.out.println("Number of Articles: " + newsAgency.getArticles().size());
        System.out.println("First Article Title: " + newsAgency.getArticles().get(0).getTitle());
        System.out.println("First Staff Member: " + newsAgency.getStaff().iterator().next().getName());
        System.out.println("-----------------------------------------\n");


        assertNotNull(newsAgency);
        assertEquals("Sample News Agency", newsAgency.getName());
        assertEquals(5, newsAgency.getStaff().size());
        assertEquals(10, newsAgency.getArticles().size());

        for (Person staffMember : newsAgency.getStaff()) {
            assertTrue(staffMember.getAge() >= 26 && staffMember.getAge() <= 65, "Staff member age out of range");
            assertNotNull(staffMember.getEmail(), "Staff email should not be null");
        }

        for (Article article : newsAgency.getArticles()) {
            assertTrue(article.getPublicationDate().isAfter(LocalDateTime.now().minusMonths(1)), "Article too old");
            assertTrue(article.getContent().length() >= 200 && article.getContent().length() <= 500, "Article content length incorrect");
            assertNotNull(article.getAuthor(), "Article author should not be null");
            assertTrue(article.getTags().size() >= 3 && article.getTags().size() <= 5, "Article tags size incorrect");
        }
    }
}

Conclusion

Instancio is a game-changer for unit and integration testing in Java. It frees you from the mundane task of manually crafting test data, allowing you to create complex, realistic, and diverse object graphs with ease. By leveraging its powerful selectors, generators, and configuration options, you can write more robust, maintainable, and readable tests.

If you’re tired of boilerplate data setup code and want to boost your testing efficiency, give Instancio a try. Your test suite (and your sanity) will thank you!

Further Resources:

Building better software starts with better engineering practices.

At N47, we don’t just write code — we engineer maintainable, well-tested, production-ready solutions. Whether you’re looking to modernize your testing strategy, accelerate development velocity, or build a new product from the ground up, our team of over 80 software experts is ready to help.

Let’s talk about your project

Leave a Reply


The reCAPTCHA verification period has expired. Please reload the page.