grades = student.getCourseGrades().values();
+ if (grades.contains(Grade.F)) {
+ throw new EnrollmentException(String.format("Student %s has at least 1 failed course", student.getId()));
+ }
+ // process enrollment...
+ return true;
+ }
+
+ public boolean enrollStudent(Student student, String courseCode) {
+ Course course = courseService.getByCode(courseCode);
+ if (course == null) {
+ throw new EnrollmentException("Course not found: " + courseCode);
+ }
+ return enrollStudent(student, course);
+ }
+
+}
diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/util/PrettyToString.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/util/PrettyToString.java
new file mode 100644
index 0000000000..163f3673c7
--- /dev/null
+++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/util/PrettyToString.java
@@ -0,0 +1,21 @@
+package com.baeldung.instancio.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+public class PrettyToString {
+
+ private static final ObjectWriter objectWriter = new ObjectMapper()
+ .registerModules(new JavaTimeModule())
+ .writerWithDefaultPrettyPrinter();
+
+ public static String toPrettyString(Object obj) {
+ try {
+ return objectWriter.writeValueAsString(obj);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/basics/CreateStudentUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/basics/CreateStudentUnitTest.java
new file mode 100644
index 0000000000..524c8dbc91
--- /dev/null
+++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/basics/CreateStudentUnitTest.java
@@ -0,0 +1,174 @@
+package com.baeldung.instancio.basics;
+
+import com.baeldung.instancio.student.model.Address;
+import com.baeldung.instancio.student.model.ContactInfo;
+import com.baeldung.instancio.student.model.Course;
+import com.baeldung.instancio.student.model.Grade;
+import com.baeldung.instancio.student.model.Phone;
+import com.baeldung.instancio.student.model.Student;
+import org.instancio.Instancio;
+import org.instancio.Model;
+import org.instancio.junit.InstancioExtension;
+import org.instancio.junit.InstancioSource;
+import org.instancio.junit.Seed;
+import org.instancio.junit.WithSettings;
+import org.instancio.settings.Keys;
+import org.instancio.settings.Settings;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+
+import java.time.LocalDate;
+import java.time.Year;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.instancio.Select.all;
+import static org.instancio.Select.field;
+
+/**
+ * Sample test class using Instancio to generate test objects.
+ *
+ * Note: using {@link InstancioExtension} is optional. The extension adds support for:
+ *
+ * - reporting seed value if a test fails
+ * - {@link Seed} annotation for reproducing failed tests
+ * - {@link WithSettings} for injecting custom settings, if needed
+ */
+@ExtendWith(InstancioExtension.class)
+class CreateStudentUnitTest {
+
+ /**
+ * Common settings to be used by all test methods.
+ */
+ @WithSettings
+ private static final Settings settings = Settings.create()
+ .set(Keys.COLLECTION_MAX_SIZE, 3);
+
+ /**
+ * A {@link Model} is a template for creating objects.
+ * Objects created from a model can be created as is, or customized, if needed.
+ */
+ private static Model studentModel() {
+ return Instancio.of(Student.class)
+ .generate(field(Student::getDateOfBirth), gen -> gen.temporal().localDate().past())
+ .generate(field(Student::getEnrollmentYear), gen -> gen.temporal().year().past())
+ .generate(field(ContactInfo::getEmail), gen -> gen.text().pattern("#a#a#a#a#a#a@example.com"))
+ .generate(field(Phone::getCountryCode), gen -> gen.string().prefix("+").digits().maxLength(2))
+ .withNullable(field(Student::getEmergencyContact))
+ .toModel();
+ }
+
+ private static void assertModelProperties(Student student) {
+ assertThat(student.getDateOfBirth()).isBefore(LocalDate.now());
+ assertThat(student.getEnrollmentYear()).isLessThan(Year.now());
+ assertThat(student.getContactInfo().getEmail()).matches("^[a-zA-Z0-9]+@example.com$");
+ assertThat(student.getContactInfo().getPhones())
+ .extracting(Phone::getCountryCode)
+ .allSatisfy(countryCode -> assertThat(countryCode).matches("^\\+\\d\\d?$"));
+ }
+
+ /**
+ * Generates random Student objects based on the Model.
+ */
+ @Test
+ void whenGivenAModel_thenShouldCreateAStudentBasedOnModel() {
+ Student student = Instancio.create(studentModel());
+
+ assertModelProperties(student);
+ }
+
+ /**
+ * Generate a list of international students based on the Model.
+ */
+ @Test
+ void whenGivenAModel_thenShouldCreateAListOfStudents() {
+ // Given
+ final int numberOfStudents = 100;
+ final List countries = Arrays.asList(
+ "China", "Germany", "India", "Poland", "Romania", "Sweden", "Switzerland");
+
+ // When
+ List studentList = Instancio.ofList(studentModel())
+ .size(numberOfStudents)
+ .generate(field(Address::getCountry), gen -> gen.oneOf(countries))
+ .create();
+
+ // Then
+ assertThat(studentList).hasSize(numberOfStudents)
+ .allSatisfy(CreateStudentUnitTest::assertModelProperties)
+ .extracting(student -> student.getContactInfo().getAddress().getCountry())
+ .allSatisfy(country -> assertThat(country).isIn(countries));
+ }
+
+ /**
+ * Use the Model to create a student with a failed course.
+ * This test also demonstrates how Instancio can provide
+ * arguments to parameterized tests.
+ *
+ * @param failedCourse provided by Instancio
+ */
+ @InstancioSource
+ @ParameterizedTest
+ void whenGivenFailingGrade_thenStudentShouldHaveAFailedCourse(final Course failedCourse) {
+ // Given
+ final Model model = studentModel();
+ final Grade failingGrade = Grade.F;
+
+ // When
+ Student student = Instancio.of(model)
+ .generate(field(Student::getCourseGrades), gen -> gen.map().with(failedCourse, failingGrade))
+ .create();
+
+ // Then
+ Map courseGrades = student.getCourseGrades();
+ assertModelProperties(student);
+ assertThat(courseGrades).containsEntry(failedCourse, failingGrade);
+ }
+
+ /**
+ * Generate a student with only Grades A and/or B.
+ */
+ @Test
+ void whenGivenGoodGrades_thenCreatedStudentShouldHaveExpectedGrades() {
+ // Given
+ final int numOfCourses = 10;
+ final Grade[] grades = {Grade.A, Grade.B};
+
+ // When
+ Student student = Instancio.of(studentModel())
+ .generate(all(Grade.class), gen -> gen.oneOf(grades))
+ .generate(field(Student::getCourseGrades), gen -> gen.map().size(numOfCourses))
+ .create();
+
+ // Then
+ Map courseGrades = student.getCourseGrades();
+ assertModelProperties(student);
+ assertThat(courseGrades.values())
+ .hasSize(numOfCourses)
+ .containsAnyOf(grades)
+ .doesNotContain(Grade.C, Grade.D, Grade.F);
+ }
+
+ /**
+ * Generate String fields prefixed with the field's name.
+ */
+ @Test
+ void whenGivenCustomSettings_thenStudentShouldBeCreatedUsingTheSettings() {
+ // Given
+ Settings customSettings = Settings.create()
+ .set(Keys.STRING_FIELD_PREFIX_ENABLED, true);
+
+ // When
+ Student student = Instancio.of(studentModel())
+ .withSettings(customSettings)
+ .create();
+
+ // Then
+ assertThat(student.getFirstName()).startsWith("firstName_");
+ assertThat(student.getLastName()).startsWith("lastName_");
+ assertThat(student.getContactInfo().getAddress().getCity()).startsWith("city_");
+ }
+}
diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/generators/UsingCustomGeneratorUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/generators/UsingCustomGeneratorUnitTest.java
new file mode 100644
index 0000000000..8e5643baec
--- /dev/null
+++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/generators/UsingCustomGeneratorUnitTest.java
@@ -0,0 +1,57 @@
+package com.baeldung.instancio.generators;
+
+import com.baeldung.instancio.student.model.Address;
+import com.baeldung.instancio.student.model.ContactInfo;
+import com.baeldung.instancio.student.model.Phone;
+import com.baeldung.instancio.student.model.Student;
+import org.instancio.Instancio;
+import org.instancio.Random;
+import org.instancio.generator.AfterGenerate;
+import org.instancio.generator.Generator;
+import org.instancio.generator.Hints;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.instancio.Select.all;
+import static org.instancio.Select.field;
+
+class UsingCustomGeneratorUnitTest {
+ private static final String ARGENTINA = "Argentina";
+ private static final int PHONES_SIZE = 5;
+
+ private static final Generator ADDRESS_GENERATOR = new Generator() {
+ @Override
+ public Address generate(final Random random) {
+ Address address = new Address();
+ address.setCountry(ARGENTINA);
+ return address;
+ }
+
+ @Override
+ public Hints hints() {
+ // The hint telling the engine to populate any field that has a null value
+ return Hints.afterGenerate(AfterGenerate.POPULATE_NULLS);
+ }
+ };
+
+ @Test
+ void whenGivenAGenerator_objectShouldBeCreatedUsingCustomGenerator() {
+ Student student = Instancio.of(Student.class)
+ .supply(all(Address.class), ADDRESS_GENERATOR)
+ .generate(field(ContactInfo::getPhones), gen -> gen.collection().size(PHONES_SIZE))
+ .create();
+
+ ContactInfo contactInfo = student.getContactInfo();
+ Address address = contactInfo.getAddress();
+ List phones = contactInfo.getPhones();
+
+ assertThat(phones).hasSize(PHONES_SIZE);
+ assertThat(address.getCountry()).isEqualTo(ARGENTINA);
+ // null fields were populated with random values
+ assertThat(address.getStreet()).isNotNull();
+ assertThat(address.getCity()).isNotNull();
+ }
+
+}
diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/generics/CreatingGenericTypesUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/generics/CreatingGenericTypesUnitTest.java
new file mode 100644
index 0000000000..4ed22abeaf
--- /dev/null
+++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/generics/CreatingGenericTypesUnitTest.java
@@ -0,0 +1,100 @@
+package com.baeldung.instancio.generics;
+
+import com.baeldung.instancio.student.model.Address;
+import org.instancio.Instancio;
+import org.instancio.TypeToken;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.instancio.Select.allLongs;
+import static org.instancio.Select.allStrings;
+
+/**
+ * Examples of creating generic types using {@link TypeToken}.
+ */
+class CreatingGenericTypesUnitTest {
+
+
+ @Test
+ void whenGivenTypeToken_shouldCreateItem() {
+ Item item = Instancio.create(new TypeToken- >() {});
+
+ assertThat(item.getValue()).isNotBlank();
+ }
+
+ @Test
+ void whenGivenTypeToken_shouldCreateCustomizedItem() {
+ Pair pair = Instancio.of(new TypeToken>() {})
+ .generate(allStrings(), gen -> gen.oneOf("foo", "bar"))
+ .generate(allLongs(), gen -> gen.longs().range(5L, 10L))
+ .create();
+
+ assertThat(pair.getLeft()).isIn("foo", "bar");
+ assertThat(pair.getRight()).isBetween(5L, 10L);
+ }
+
+ @Test
+ void whenGivenTypeToken_shouldCreateTriplet() {
+ Triplet triplet = Instancio.create(new TypeToken>() {});
+
+ assertThat(triplet.getLeft()).isNotBlank();
+ assertThat(triplet.getMiddle()).isNotNull();
+ assertThat(triplet.getRight()).isNotNull();
+ }
+
+ @Test
+ void whenGivenTypeToken_shouldCreateCollection() {
+ List list = Instancio.create(new TypeToken
>() {});
+
+ assertThat(list).isNotEmpty().doesNotContainNull();
+ }
+
+ @Test
+ void whenGivenTypeToken_shouldCreateMap() {
+ Map map = Instancio.create(new TypeToken