BAEL-6164: Generate unit test data with Instancio (#13350)

This commit is contained in:
Arman Sharif 2023-01-26 16:29:47 -08:00 committed by GitHub
parent 69008b0f0d
commit 7f3f707077
30 changed files with 1212 additions and 0 deletions

14
testing-modules/instancio/.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
*.class
.settings
.project
#folders#
/target
/src/main/webapp/WEB-INF/classes
*/META-INF/*
# Packaged files #
*.jar
*.war
*.ear

View File

@ -0,0 +1 @@
### Relevant articles

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>instancio</artifactId>
<name>instancio</name>
<packaging>jar</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>testing-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.instancio</groupId>
<artifactId>instancio-junit</artifactId>
<version>${instancio.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<instancio.version>2.6.0</instancio.version>
<jackson.version>2.14.1</jackson.version>
<junit-jupiter.version>5.9.2</junit-jupiter.version>
</properties>
</project>

View File

@ -0,0 +1,6 @@
package com.baeldung.instancio.abstracttype;
public interface AbstractItem<T> {
T getValue();
}

View File

@ -0,0 +1,18 @@
package com.baeldung.instancio.generics;
import com.baeldung.instancio.abstracttype.AbstractItem;
public class Item<T> implements AbstractItem<T> {
private T value;
@Override
public T getValue() {
return value;
}
@Override
public String toString() {
return String.format("Item[value=%s]", value);
}
}

View File

@ -0,0 +1,20 @@
package com.baeldung.instancio.generics;
public class Pair<L, R> {
private L left;
private R right;
public L getLeft() {
return left;
}
public R getRight() {
return right;
}
@Override
public String toString() {
return String.format("Pair[left=%s, right=%s]", left, right);
}
}

View File

@ -0,0 +1,24 @@
package com.baeldung.instancio.generics;
public class Triplet<L, M, R> {
private L left;
private M middle;
private R right;
public L getLeft() {
return left;
}
public M getMiddle() {
return middle;
}
public R getRight() {
return right;
}
@Override
public String toString() {
return String.format("Triplet[left=%s, middle=%s, right=%s]", left, right, middle);
}
}

View File

@ -0,0 +1,31 @@
package com.baeldung.instancio.student.model;
import com.baeldung.instancio.util.PrettyToString;
public class Address {
private String street;
private String city;
private String country;
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getCountry() {
return country;
}
public void setCountry(final String country) {
this.country = country;
}
@Override
public String toString() {
return PrettyToString.toPrettyString(this);
}
}

View File

@ -0,0 +1,28 @@
package com.baeldung.instancio.student.model;
import com.baeldung.instancio.util.PrettyToString;
import java.util.List;
public class ContactInfo {
private Address address;
private List<Phone> phones;
private String email;
public Address getAddress() {
return address;
}
public List<Phone> getPhones() {
return phones;
}
public String getEmail() {
return email;
}
@Override
public String toString() {
return PrettyToString.toPrettyString(this);
}
}

View File

@ -0,0 +1,53 @@
package com.baeldung.instancio.student.model;
import com.baeldung.instancio.util.PrettyToString;
import java.time.Duration;
import java.time.LocalDate;
import java.util.Objects;
public class Course {
private String title;
private String code;
private LocalDate startDate;
private Duration duration;
private String instructor;
public String getTitle() {
return title;
}
public String getCode() {
return code;
}
public LocalDate getStartDate() {
return startDate;
}
public Duration getDuration() {
return duration;
}
public String getInstructor() {
return instructor;
}
@Override
public String toString() {
return PrettyToString.toPrettyString(this);
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof Course)) return false;
final Course course = (Course) o;
return Objects.equals(getCode(), course.getCode());
}
@Override
public int hashCode() {
return Objects.hash(getCode());
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.instancio.student.model;
public class EmergencyContact {
private String name;
private Phone phone;
public String getName() {
return name;
}
public Phone getPhone() {
return phone;
}
}

View File

@ -0,0 +1,5 @@
package com.baeldung.instancio.student.model;
public enum Grade {
A, B, C, D, F
}

View File

@ -0,0 +1,23 @@
package com.baeldung.instancio.student.model;
import com.baeldung.instancio.util.PrettyToString;
public class Phone {
private String countryCode;
private String number;
public String getCountryCode() {
return countryCode;
}
public String getNumber() {
return number;
}
@Override
public String toString() {
return PrettyToString.toPrettyString(this);
}
}

View File

@ -0,0 +1,57 @@
package com.baeldung.instancio.student.model;
import com.baeldung.instancio.util.PrettyToString;
import java.time.LocalDate;
import java.time.Year;
import java.util.Map;
import java.util.UUID;
public class Student {
private UUID id;
private String firstName;
private String lastName;
private LocalDate dateOfBirth;
private ContactInfo contactInfo;
private EmergencyContact emergencyContact;
private Year enrollmentYear;
private Map<Course, Grade> courseGrades;
public UUID getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
public ContactInfo getContactInfo() {
return contactInfo;
}
public EmergencyContact getEmergencyContact() {
return emergencyContact;
}
public Year getEnrollmentYear() {
return enrollmentYear;
}
public Map<Course, Grade> getCourseGrades() {
return courseGrades;
}
@Override
public String toString() {
return PrettyToString.toPrettyString(this);
}
}

View File

@ -0,0 +1,11 @@
package com.baeldung.instancio.student.service;
import com.baeldung.instancio.student.model.Course;
public class CourseService {
public Course getByCode(String courseCode) {
throw new UnsupportedOperationException(
"This class should be mocked. Persistence is not available in a unit test");
}
}

View File

@ -0,0 +1,8 @@
package com.baeldung.instancio.student.service;
public class EnrollmentException extends RuntimeException {
public EnrollmentException(final String message) {
super(message);
}
}

View File

@ -0,0 +1,30 @@
package com.baeldung.instancio.student.service;
import com.baeldung.instancio.student.model.Course;
import com.baeldung.instancio.student.model.Grade;
import com.baeldung.instancio.student.model.Student;
import java.util.Collection;
public class EnrollmentService {
private CourseService courseService;
public boolean enrollStudent(Student student, Course course) {
Collection<Grade> 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);
}
}

View File

@ -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);
}
}
}

View File

@ -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.
*
* <p>Note: using {@link InstancioExtension} is optional. The extension adds support for:
* <p>
* - 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<Student> 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<String> countries = Arrays.asList(
"China", "Germany", "India", "Poland", "Romania", "Sweden", "Switzerland");
// When
List<Student> 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<Student> 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<Course, Grade> 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<Course, Grade> 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_");
}
}

View File

@ -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> ADDRESS_GENERATOR = new Generator<Address>() {
@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<Phone> 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();
}
}

View File

@ -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<String> item = Instancio.create(new TypeToken<Item<String>>() {});
assertThat(item.getValue()).isNotBlank();
}
@Test
void whenGivenTypeToken_shouldCreateCustomizedItem() {
Pair<String, Long> pair = Instancio.of(new TypeToken<Pair<String, Long>>() {})
.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<String, UUID, Long> triplet = Instancio.create(new TypeToken<Triplet<String, UUID, Long>>() {});
assertThat(triplet.getLeft()).isNotBlank();
assertThat(triplet.getMiddle()).isNotNull();
assertThat(triplet.getRight()).isNotNull();
}
@Test
void whenGivenTypeToken_shouldCreateCollection() {
List<String> list = Instancio.create(new TypeToken<List<String>>() {});
assertThat(list).isNotEmpty().doesNotContainNull();
}
@Test
void whenGivenTypeToken_shouldCreateMap() {
Map<UUID, Address> map = Instancio.create(new TypeToken<Map<UUID, Address>>() {});
assertThat(map).isNotEmpty();
}
/**
* Using type token to create more complex generic objects.
*/
@Test
void whenGivenTypeTokenWithNestGenerics_shouldCreateAnInstanceOfSpecifiedType() {
List<Triplet<Integer, LocalDate, Item<String>>> list = Instancio.create(
new TypeToken<List<Triplet<Integer, LocalDate, Item<String>>>>() {});
assertThat(list)
.isNotEmpty()
.allSatisfy(triplet -> {
assertThat(triplet.getLeft()).isInstanceOf(Integer.class);
assertThat(triplet.getMiddle()).isInstanceOf(LocalDate.class);
assertThat(triplet.getRight())
.isInstanceOf(Item.class)
.satisfies(item -> assertThat(item.getValue()).isNotBlank());
});
// Sample output
list.forEach(System.out::println);
}
/**
* Alternative way to create generic objects is using 'withTypeParameters'.
* However, this approach generates an "unchecked assignment" warning.
*/
@Test
@SuppressWarnings("unchecked")
void whenGivenClassWithTypeParameters_shouldCreateGenericType() {
Map<UUID, Address> map = Instancio.of(Map.class)
.withTypeParameters(UUID.class, Address.class)
.create();
assertThat(map).isNotEmpty();
}
}

View File

@ -0,0 +1,45 @@
package com.baeldung.instancio.mockito;
import com.baeldung.instancio.student.model.Course;
import com.baeldung.instancio.student.model.Grade;
import com.baeldung.instancio.student.model.Student;
import com.baeldung.instancio.student.service.CourseService;
import com.baeldung.instancio.student.service.EnrollmentService;
import org.instancio.Instancio;
import org.instancio.junit.InstancioExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.instancio.Select.all;
import static org.mockito.Mockito.when;
@ExtendWith({MockitoExtension.class, InstancioExtension.class})
class InstancioWithMockitoUnitTest {
@Mock
private CourseService courseService;
@InjectMocks
private EnrollmentService enrollmentService;
@Test
void givenStudentWithoutGradeF_thenShouldEnrollStudentInCourse() {
// Given
Student student = Instancio.of(Student.class)
.generate(all(Grade.class), gen -> gen.enumOf(Grade.class).excluding(Grade.F))
.create();
Course course = Instancio.create(Course.class);
when(courseService.getByCode(course.getCode())).thenReturn(course);
// When
boolean isEnrolled = enrollmentService.enrollStudent(student, course.getCode());
// Then
assertThat(isEnrolled).isTrue();
}
}

View File

@ -0,0 +1,60 @@
package com.baeldung.instancio.reproducing;
import com.baeldung.instancio.student.model.Course;
import com.baeldung.instancio.student.model.Student;
import com.baeldung.instancio.student.service.EnrollmentService;
import org.instancio.Instancio;
import org.instancio.junit.InstancioExtension;
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.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(InstancioExtension.class)
class ReproducingTestFailureUnitTest {
@WithSettings
private static final Settings settings = Settings.create()
.set(Keys.COLLECTION_MIN_SIZE, 50)
.set(Keys.COLLECTION_MAX_SIZE, 100)
.lock();
private final EnrollmentService enrollmentService = new EnrollmentService();
/**
* This test fails because the {@code enrollInCourse()} method
* throws an exception if the student has at least one grade F.
*
* <p>Sample error message generated by {@link InstancioExtension}:
*
* <pre>
* timestamp = 2023-01-24T13:50:12.436704221, Instancio = Test method 'enrollStudent' failed with seed: 1234
* </pre>
* <p>
* Using the reported seed value we can reproduce the test failure.
*/
@Test
@Seed(1234)
@Disabled("This test fails on purpose to demonstrate failure reporting by InstancioExtension")
void whenGivenNoFailingGrades_thenShouldEnrollStudentInCourse() {
// Given
Course course = Instancio.create(Course.class);
Student student = Instancio.of(Student.class)
// The test can be fixed by uncommenting the line below:
//.generate(all(Grade.class), gen -> gen.enumOf(Grade.class).excluding(Grade.F))
.create();
System.out.println(student); // same data generated on each run
// When
boolean isEnrolled = enrollmentService.enrollStudent(student, course);
// Then
assertThat(isEnrolled).isTrue();
}
}

View File

@ -0,0 +1,76 @@
package com.baeldung.instancio.selectors;
import com.baeldung.instancio.student.model.ContactInfo;
import com.baeldung.instancio.student.model.EmergencyContact;
import com.baeldung.instancio.student.model.Phone;
import com.baeldung.instancio.student.model.Student;
import org.instancio.Instancio;
import org.instancio.Scope;
import org.instancio.Select;
import org.instancio.TargetSelector;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.instancio.Select.allStrings;
import static org.instancio.Select.scope;
/**
* Examples of various selector {@link Scope}.
* Scopes allow narrowing down selector targets.
*/
class SelectorScopesUnitTest {
/**
* Prefix all String fields in Phone class with "phone_".
*/
@Test
void whenGivenClassScope_shouldSelectTargetsWithinClass() {
// Given
final String prefix = "phone_";
final Scope phoneClass = scope(Phone.class);
// When
Student student = Instancio.of(Student.class)
.generate(allStrings().within(phoneClass), gen -> gen.string().prefix(prefix))
.create();
// Then
// matches phone numbers
Phone emergencyContactPhone = student.getEmergencyContact().getPhone();
assertThat(emergencyContactPhone.getCountryCode()).startsWith(prefix);
assertThat(emergencyContactPhone.getNumber()).startsWith(prefix);
assertThat(student.getContactInfo().getPhones()).allSatisfy(phone -> {
assertThat(phone.getCountryCode()).startsWith(prefix);
assertThat(phone.getNumber()).startsWith(prefix);
});
// does not match other fields
assertThat(student.getContactInfo().getAddress().getCity()).doesNotStartWith(prefix);
}
/**
* Using scope to set student's and their emergency contact's
* phone number to different values.
*/
@Test
void whenGivenFieldScope_shouldSelectTargetsWithinField() {
// Given
TargetSelector studentPhone = Select.field(Phone::getNumber).within(scope(ContactInfo.class));
TargetSelector emergencyPhone = Select.field(Phone::getNumber).within(scope(EmergencyContact.class));
// When
Student student = Instancio.of(Student.class)
.set(studentPhone, "student")
.set(emergencyPhone, "emergency")
.create();
// Then
assertThat(student.getContactInfo().getPhones())
.isNotEmpty()
.allSatisfy(phone -> assertThat(phone.getNumber()).isEqualTo("student"));
assertThat(student.getEmergencyContact().getPhone().getNumber())
.isEqualTo("emergency");
}
}

View File

@ -0,0 +1,104 @@
package com.baeldung.instancio.selectors;
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.FieldSelectorBuilder;
import org.instancio.Instancio;
import org.instancio.Select;
import org.instancio.Selector;
import org.instancio.SelectorGroup;
import org.instancio.TargetSelector;
import org.junit.jupiter.api.Test;
import java.util.Collection;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.instancio.Select.all;
import static org.instancio.Select.field;
import static org.instancio.Select.fields;
import static org.instancio.Select.types;
/**
* Examples of various types of selectors provided by the {@link Select} class.
*/
class SelectorsUnitTest {
@Test
void whenGivenFieldSelector_shouldCustomizeSelectedField() {
Address address = Instancio.of(Address.class)
.set(field(Address::getCity), "London")
.set(field(Address.class, "country"), "UK")
.create();
assertThat(address.getCity()).isEqualTo("London");
assertThat(address.getCountry()).isEqualTo("UK");
}
@Test
void whenGivenClassSelector_shouldCustomizeSelectedClass() {
// Given
final Selector allStrings = all(String.class);
final String prefix = "test_";
// When
Address address = Instancio.of(Address.class)
.generate(allStrings, gen -> gen.string().prefix(prefix))
.create();
// Then
assertThat(address.getCity()).startsWith(prefix);
assertThat(address.getCountry()).startsWith(prefix);
}
@Test
void whenGivenPredicateFieldSelector_shouldCustomizeMatchingFields() {
// Given: regie matching 'city' and 'country' fields
final FieldSelectorBuilder fieldsMatchingRegex = fields().matching("c.*y");
// When
Address address = Instancio.of(Address.class)
.ignore(fieldsMatchingRegex)
.create();
// Then
assertThat(address.getCity()).isNull();
assertThat(address.getCountry()).isNull();
assertThat(address.getStreet()).isNotBlank();
}
@Test
void whenGivenPredicateClassSelector_shouldCustomizeMatchingClasses() {
// Given
final TargetSelector allTypesOfCollections = types().of(Collection.class);
final int size = 3;
// When
ContactInfo contactInfo = Instancio.of(ContactInfo.class)
.generate(allTypesOfCollections, gen -> gen.collection().size(size))
.create();
// Then
List<Phone> phones = contactInfo.getPhones();
assertThat(phones).hasSize(size);
}
@Test
void whenGivenSelectorGroup_shouldCustomizeSelectedFields() {
// Given
SelectorGroup ignoredFields = all(
field(Student::getId),
field(Student::getDateOfBirth));
// When
Student student = Instancio.of(Student.class)
.ignore(ignoredFields)
.create();
// Then
assertThat(student.getId()).isNull();
assertThat(student.getDateOfBirth()).isNull();
}
}

View File

@ -0,0 +1,67 @@
package com.baeldung.instancio.settings;
import com.baeldung.instancio.student.model.ContactInfo;
import com.baeldung.instancio.student.model.Phone;
import org.instancio.Instancio;
import org.instancio.junit.InstancioExtension;
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 java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* {@link InstancioExtension} allows injecting custom settings
* using the {@link WithSettings} annotation.
*/
@ExtendWith(InstancioExtension.class)
class CustomSettingsUnitTest {
private static final int MIN_SIZE = 0;
private static final int MAX_SIZE = 3;
/**
* Common settings to be used by all test methods.
*/
@WithSettings
private static final Settings settings = Settings.create()
.set(Keys.COLLECTION_MIN_SIZE, MIN_SIZE)
.set(Keys.COLLECTION_MAX_SIZE, MAX_SIZE)
.lock();
@Test
void whenGivenInjectedSettings_shouldUseCustomSettings() {
ContactInfo info = Instancio.create(ContactInfo.class);
List<Phone> phones = info.getPhones();
assertThat(phones).hasSizeBetween(MIN_SIZE, MAX_SIZE);
}
@Test
void whenSettingsOverridden_shouldUseTheOverrides() {
// Given
final int collectionSize = 50;
Settings additionalSettings = Settings.create()
.set(Keys.STRING_FIELD_PREFIX_ENABLED, true)
.set(Keys.COLLECTION_MIN_SIZE, collectionSize)
.set(Keys.COLLECTION_MAX_SIZE, collectionSize);
// When
ContactInfo info = Instancio.of(ContactInfo.class)
.withSettings(additionalSettings)
.create();
// Then
assertThat(info.getPhones())
.hasSize(collectionSize)
.allSatisfy(phone -> {
assertThat(phone.getCountryCode()).startsWith("countryCode_");
assertThat(phone.getNumber()).startsWith("number_");
});
}
}

View File

@ -0,0 +1,50 @@
package com.baeldung.instancio.settings;
import org.instancio.Instancio;
import org.instancio.settings.Keys;
import org.instancio.settings.Settings;
import org.junit.jupiter.api.Test;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
class UsingInstancioPropertiesUnitTest {
/**
* Instancio automatically loads {@code instancio.properties} file,
* if it's present, from the root of the classpath.
*
* <p>Float range was overridden to [0, 100].
* See: {@code src/test/resources/instancio.properties}
*/
@Test
void whenInstancioPropertiesAreOnClasspath_shouldUseConfiguredProperties() {
Set<Float> floats = Instancio.ofSet(Float.class).create();
assertThat(floats)
.isNotEmpty()
.allSatisfy(f -> assertThat(f).isBetween(0f, 100f));
}
/**
* We can override global configuration using {@link Settings}.
*/
@Test
void whenCustomSettingsAreProvided_shouldOverrideInstancioProperties() {
// Given
Settings settings = Settings.create()
.set(Keys.FLOAT_MIN, 100f)
.set(Keys.FLOAT_MAX, 200f);
// When
Set<Float> floats = Instancio.ofSet(Float.class)
.withSettings(settings)
.create();
// Then
assertThat(floats)
.isNotEmpty()
.allSatisfy(f -> assertThat(f).isBetween(100f, 200f));
}
}

View File

@ -0,0 +1,54 @@
package com.baeldung.instancio.subtype;
import com.baeldung.instancio.abstracttype.AbstractItem;
import com.baeldung.instancio.generics.Item;
import com.baeldung.instancio.student.model.ContactInfo;
import com.baeldung.instancio.student.model.Student;
import org.instancio.Instancio;
import org.instancio.TypeToken;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.LinkedList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.instancio.Select.all;
import static org.instancio.Select.field;
/**
* Using {@code subtype()} method we can specify a specific implementation
* class for an abstract type, or a specialized subclass for a concrete class.
*/
class SubtypeUnitTest {
@Test
void whenGivenCollectionSubtype_shouldUseSpecifiedCollectionClass() {
// Given
final Class<?> subtype = LinkedList.class;
// When
Student student = Instancio.of(Student.class)
.subtype(field(ContactInfo::getPhones), subtype)
.create();
// Then
assertThat(student.getContactInfo().getPhones())
.isNotEmpty()
.isExactlyInstanceOf(subtype);
}
@Test
void whenGivenSubtypeForGenericAbstractType_shouldUseSpecifiedConcreteClass() {
// Given
final Class<?> subtype = Item.class;
// When
AbstractItem<LocalDateTime> abstractItem = Instancio.of(new TypeToken<AbstractItem<LocalDateTime>>() {})
.subtype(all(AbstractItem.class), subtype)
.create();
// Then
assertThat(abstractItem).isExactlyInstanceOf(subtype);
assertThat(abstractItem.getValue()).isNotNull();
}
}

View File

@ -0,0 +1,2 @@
float.min=1
float.max=100

View File

@ -22,6 +22,7 @@
<module>gatling</module>
<module>groovy-spock</module>
<module>hamcrest</module>
<module>instancio</module>
<module>junit-4</module>
<module>junit-5-advanced</module>
<module>junit-5-basics</module>