BAEL-6164: Generate unit test data with Instancio (#13350)
This commit is contained in:
parent
69008b0f0d
commit
7f3f707077
14
testing-modules/instancio/.gitignore
vendored
Normal file
14
testing-modules/instancio/.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
*.class
|
||||
|
||||
.settings
|
||||
.project
|
||||
|
||||
#folders#
|
||||
/target
|
||||
/src/main/webapp/WEB-INF/classes
|
||||
*/META-INF/*
|
||||
|
||||
# Packaged files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
1
testing-modules/instancio/README.md
Normal file
1
testing-modules/instancio/README.md
Normal file
@ -0,0 +1 @@
|
||||
### Relevant articles
|
58
testing-modules/instancio/pom.xml
Normal file
58
testing-modules/instancio/pom.xml
Normal 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>
|
@ -0,0 +1,6 @@
|
||||
package com.baeldung.instancio.abstracttype;
|
||||
|
||||
public interface AbstractItem<T> {
|
||||
|
||||
T getValue();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.baeldung.instancio.student.model;
|
||||
|
||||
public enum Grade {
|
||||
A, B, C, D, F
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.baeldung.instancio.student.service;
|
||||
|
||||
public class EnrollmentException extends RuntimeException {
|
||||
|
||||
public EnrollmentException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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_");
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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_");
|
||||
});
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
float.min=1
|
||||
float.max=100
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user