first commit
This commit is contained in:
commit
e12f3b284d
11 changed files with 663 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### Example user template template
|
||||||
|
### Example user template
|
||||||
|
|
||||||
|
# IntelliJ project files
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
out
|
||||||
|
gen
|
||||||
|
target/
|
||||||
8
README.md
Normal file
8
README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
This project is called Edie after Edie Sedgwick who was one of the artists in Andy Warhols Factory.
|
||||||
|
The film about her life is called Factory Girl (2006).
|
||||||
|
This project was inspired by the FactoryGirl project that was later renamed FactoryBot.
|
||||||
|
|
||||||
|
|
||||||
|
* https://en.wikipedia.org/wiki/Edie_Sedgwick
|
||||||
|
* https://www.imdb.com/title/tt0432402
|
||||||
|
* https://hn.algolia.com/?q=factorybot
|
||||||
47
pom.xml
Normal file
47
pom.xml
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?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>
|
||||||
|
|
||||||
|
<groupId>nl.sander</groupId>
|
||||||
|
<artifactId>edie</artifactId>
|
||||||
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>info.cukes</groupId>
|
||||||
|
<artifactId>cucumber-core</artifactId>
|
||||||
|
<version>1.2.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.5.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.0.0-M4</version>
|
||||||
|
<configuration>
|
||||||
|
<includes>
|
||||||
|
<include>**/*Tests.java</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
9
src/main/java/shautvast/edie/Adapter.java
Normal file
9
src/main/java/shautvast/edie/Adapter.java
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
package shautvast.edie;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Adapter lambda is used to alter instances after creation according to the template.
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
public interface Adapter<T> {
|
||||||
|
public void adapt(T input);
|
||||||
|
}
|
||||||
127
src/main/java/shautvast/edie/Definition.java
Normal file
127
src/main/java/shautvast/edie/Definition.java
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
package shautvast.edie;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the specification for creating instances. They are either Supplier lambda or a Constructor instance.
|
||||||
|
*/
|
||||||
|
public class Definition<T> {
|
||||||
|
|
||||||
|
/* contains the class instances for creating elements in a List */
|
||||||
|
private final List<Class<?>> typesInList = new ArrayList<>();
|
||||||
|
|
||||||
|
/* type of the object that will be created*/
|
||||||
|
private Class<T> type;
|
||||||
|
|
||||||
|
/* constructor class for creating instances*/
|
||||||
|
private Constructor<T> constructor;
|
||||||
|
|
||||||
|
/* lambda that can be used to create instances*/
|
||||||
|
private Supplier<T> template;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructor that takes a type. The constructor member variable must be added later on.
|
||||||
|
*
|
||||||
|
* @param type Type of the instances this Definition will create.
|
||||||
|
*/
|
||||||
|
Definition(Class<T> type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructor that takes a Supplier lambda to create instances.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
*/
|
||||||
|
Definition(Supplier<T> supplier) {
|
||||||
|
this.template = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to create the instance, once the definition is constructed.
|
||||||
|
* Can be called directly, or via the Factory.build() method
|
||||||
|
*
|
||||||
|
* @return a new instance by calling the supplier or a constructor.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T build() {
|
||||||
|
if (template != null) {
|
||||||
|
return template.get();
|
||||||
|
} else if (constructor != null) {
|
||||||
|
if (!typesInList.isEmpty()) {
|
||||||
|
return (T) newInstance(new Object[]{
|
||||||
|
typesInList.stream()
|
||||||
|
.map(Factory::build)
|
||||||
|
.collect(Collectors.toList())});
|
||||||
|
} else {
|
||||||
|
Object[] args = Arrays.stream(constructor.getParameterTypes())
|
||||||
|
.map(Factory::build)
|
||||||
|
.collect(Collectors.toList()).toArray(new Object[]{});
|
||||||
|
return (T) newInstance(args);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Template and constructor cannot both be empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build method that takes an adapter lambda for customizing the final object
|
||||||
|
* Example:
|
||||||
|
* Definition<Person> personDef = define(Person.class, () -> new Person("Sander", 48));
|
||||||
|
* Person person = personDef.build(p -> {
|
||||||
|
* p.setName("Harry");
|
||||||
|
* return p;
|
||||||
|
* });
|
||||||
|
* @param adapter
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public T build(Adapter<T> adapter) {
|
||||||
|
T instance = template.get();
|
||||||
|
adapter.adapt(instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object newInstance(Object[] args) {
|
||||||
|
try {
|
||||||
|
return constructor.newInstance(args);
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example:
|
||||||
|
* Definition<Employee> employeeDef = Factory.define(Employee.class).withConstructorArgs(Person.class);
|
||||||
|
* Can be used given that Employee has a constructor with one parameterof type Person
|
||||||
|
* This will use this constructor to create instances.
|
||||||
|
*
|
||||||
|
* @param parameterTypes varargs argument for the parameters of the constructor to look up.
|
||||||
|
*
|
||||||
|
* @return a Definition with the correct constructor
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public Definition<T> withConstructorArgs(Class... parameterTypes) {
|
||||||
|
try {
|
||||||
|
this.constructor = type.getConstructor(parameterTypes);
|
||||||
|
return this;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Definition<T> withConstructorArgs(List<Class<?>> typesToConstruct) {
|
||||||
|
try {
|
||||||
|
this.constructor = type.getConstructor(List.class);
|
||||||
|
this.typesInList.addAll(typesToConstruct);
|
||||||
|
return this;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
100
src/main/java/shautvast/edie/Factory.java
Normal file
100
src/main/java/shautvast/edie/Factory.java
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
package shautvast.edie;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes","unchecked"})
|
||||||
|
public class Factory {
|
||||||
|
|
||||||
|
private static final ConcurrentMap<Class<?>, Definition> definitions = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a definition for the given type, using a Supplier that will create that type every time build() is called.
|
||||||
|
*
|
||||||
|
* @param type Class that specifies what type must be created
|
||||||
|
* @param supplier Lambda that creates the given type
|
||||||
|
* @param <T> Generic type of the created Definition
|
||||||
|
* @return A Definition that can be called directly to create Type instances
|
||||||
|
*/
|
||||||
|
public static <T> Definition<T> define(Class<T> type, Supplier<T> supplier) {
|
||||||
|
Definition<T> definition = new Definition<>(supplier);
|
||||||
|
definitions.put(type, definition);
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a definition for the given type.
|
||||||
|
* when using this method, the resulting definition is not complete yet. See Definition.withConstructor.
|
||||||
|
*
|
||||||
|
* @param type Class that specifies what type must be created
|
||||||
|
* @param <T> Generic type of the created Definition
|
||||||
|
* @return A Definition that can be called directly to create Type instances
|
||||||
|
*/
|
||||||
|
public static <T> Definition<T> define(Class<T> type) {
|
||||||
|
Definition<T> definition = new Definition<>(type);
|
||||||
|
definitions.put(type, definition);
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the instance for the specified type, using an Adapter lambda that can alter specific attributes if needed.
|
||||||
|
* It's logic is delegated to the Definition for the type.
|
||||||
|
* <p>
|
||||||
|
* Example:
|
||||||
|
* Definition<Person> personDef = define(Person.class, () -> new Person("Sander", 48));
|
||||||
|
* <p>
|
||||||
|
* Person person = personDef.build(p -> {
|
||||||
|
* p.setName("Harry");
|
||||||
|
* return p;
|
||||||
|
* });
|
||||||
|
* <p>
|
||||||
|
* The template name is 'Sander' but the person instance has a different name.
|
||||||
|
*
|
||||||
|
* @param type Class to denote the type to create.
|
||||||
|
* @param adapter Can be used to change the type after creation
|
||||||
|
* @return an instance of the given type using template and adapter
|
||||||
|
*/
|
||||||
|
public static <T> T build(Class<T> type, Adapter<T> adapter) {
|
||||||
|
return getDefinition(type).build(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the instance for the specified type.
|
||||||
|
* It's logic is delegated to the Definition for the type.
|
||||||
|
* <p>
|
||||||
|
* Example:
|
||||||
|
* Definition<Person> personDef = define(Person.class, () -> new Person("Sander", 48));
|
||||||
|
* <p>
|
||||||
|
* Person person = personDef.build()
|
||||||
|
* will always yields a new instance
|
||||||
|
*
|
||||||
|
* @param type Class to denote the type to create.
|
||||||
|
* @return an instance of the given type using template and adapter
|
||||||
|
*/
|
||||||
|
public static <T> T build(Class<T> type) {
|
||||||
|
return getDefinition(type).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> Definition<T> getDefinition(Class<T> type) {
|
||||||
|
Definition<T> definition = definitions.get(type);
|
||||||
|
if (definition == null) {
|
||||||
|
throw new IllegalStateException("definition not found for " + type);
|
||||||
|
} else {
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkTypes(Class<?>[] typesToConstruct) {
|
||||||
|
if (Arrays.stream(typesToConstruct)
|
||||||
|
.anyMatch(type -> !definitions.containsKey(type))) {
|
||||||
|
throw new IllegalStateException("not all parameters bound to definition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
106
src/main/java/shautvast/edie/FactoryHelper.java
Normal file
106
src/main/java/shautvast/edie/FactoryHelper.java
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
package shautvast.edie;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.atomic.LongAdder;
|
||||||
|
|
||||||
|
public class FactoryHelper {
|
||||||
|
|
||||||
|
/* unnamed counter */
|
||||||
|
private static LongAdder globalCounter = new LongAdder();
|
||||||
|
/* contains named counters */
|
||||||
|
private static ConcurrentMap<String, LongAdder> namedCounters = new ConcurrentHashMap<>();
|
||||||
|
/* util for random numbers*/
|
||||||
|
private static Random random = new Random();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Values that need an index can be created with this helper method.
|
||||||
|
* <p>
|
||||||
|
* Example:
|
||||||
|
* Factory.define(Person.class, () -> new Person("person[" + increment()+"]", 33));
|
||||||
|
*
|
||||||
|
* @return a number that increments on every invocation of the type supplier.
|
||||||
|
*/
|
||||||
|
public static long increment() {
|
||||||
|
return incrementAndGet(globalCounter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an named incrementer that is called every time the Factory creates the object.
|
||||||
|
* <p>
|
||||||
|
* Example:
|
||||||
|
* Factory.define(Person.class, () -> new Person("person[" + increment("personIndex")+"]", 33));
|
||||||
|
*
|
||||||
|
* @param name the name of the incrementer
|
||||||
|
* @return a number that increments every time this method is called with the same name.
|
||||||
|
*/
|
||||||
|
public static long increment(String name) {
|
||||||
|
return incrementAndGet(namedCounters.computeIfAbsent(name, k -> new LongAdder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random 64 bit integer number.
|
||||||
|
* <p>
|
||||||
|
* Example:
|
||||||
|
* define(Person.class, () -> new Person("Sander", randomLong()));
|
||||||
|
*
|
||||||
|
* @return a random number on every invocation
|
||||||
|
*/
|
||||||
|
public static long randomLong() {
|
||||||
|
return random.nextLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random 32 bit integer number.
|
||||||
|
* <p>
|
||||||
|
* Example:
|
||||||
|
* define(Person.class, () -> new Person("Sander", randomInt()));
|
||||||
|
*
|
||||||
|
* @return a random number on every invocation
|
||||||
|
*/
|
||||||
|
public static int randomInt() {
|
||||||
|
return random.nextInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random 32 bit integer number below an upper bound.
|
||||||
|
* <p>
|
||||||
|
* Example:
|
||||||
|
* define(Person.class, () -> new Person("Sander", randomInt(100)));
|
||||||
|
*
|
||||||
|
* @param bound exlusive upper bound
|
||||||
|
* @return a random number on every invocation
|
||||||
|
*/
|
||||||
|
public static int randomInt(int bound) {
|
||||||
|
return random.nextInt(bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random 64 bit floating point number below an upper bound.
|
||||||
|
*
|
||||||
|
* @return a random number on every invocation
|
||||||
|
*/
|
||||||
|
public static double randomDouble() {
|
||||||
|
return random.nextDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for the creation of factory types within a list.
|
||||||
|
*
|
||||||
|
* @param typesToConstruct Any types (should be known in Factory) that are to be elements in a List
|
||||||
|
* @return A List of the same types. See
|
||||||
|
*/
|
||||||
|
public static List<Class<?>> listOf(Class<?>... typesToConstruct) {
|
||||||
|
Factory.checkTypes(typesToConstruct);
|
||||||
|
return Arrays.asList(typesToConstruct);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long incrementAndGet(LongAdder longAdder) {
|
||||||
|
long value = longAdder.longValue();
|
||||||
|
longAdder.increment();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
143
src/test/java/shautvast/edie/FactoryTests.java
Normal file
143
src/test/java/shautvast/edie/FactoryTests.java
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
package shautvast.edie;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import shautvast.edie.testdomain.Company;
|
||||||
|
import shautvast.edie.testdomain.Employee;
|
||||||
|
import shautvast.edie.testdomain.Person;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@DisplayName("Assertions for the factory and definitions")
|
||||||
|
public class FactoryTests {
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should build instance with definition.build()")
|
||||||
|
public void simpleTemplate() {
|
||||||
|
Definition<Person> personDef = Factory.define(Person.class, () -> new Person("Sander", 48));
|
||||||
|
|
||||||
|
Person person = personDef.build();
|
||||||
|
|
||||||
|
Assertions.assertEquals("Sander", person.getName());
|
||||||
|
Assertions.assertEquals(48, person.getAge());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should build instance with Factory.build()")
|
||||||
|
public void simpleTemplateStatic() {
|
||||||
|
Factory.define(Person.class, () -> new Person("Sander", 48));
|
||||||
|
|
||||||
|
Person person = Factory.build(Person.class);
|
||||||
|
|
||||||
|
Assertions.assertEquals("Sander", person.getName());
|
||||||
|
Assertions.assertEquals(48, person.getAge());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should build a customized instance while the other attributes are according to the template")
|
||||||
|
public void simpleTemplateAlteration() {
|
||||||
|
Factory.define(Person.class, () -> new Person("Sander", 48));
|
||||||
|
|
||||||
|
Person person = Factory.build(Person.class, p -> p.setName("Harry"));
|
||||||
|
|
||||||
|
Assertions.assertEquals("Harry", person.getName());
|
||||||
|
Assertions.assertEquals(48, person.getAge());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should create different values for an attribute for every instance")
|
||||||
|
public void counters() {
|
||||||
|
Definition<Person> personDef = Factory.define(Person.class, () -> new Person("Sander", 48));
|
||||||
|
|
||||||
|
Person person = personDef.build(p ->
|
||||||
|
p.setName("Harry" + FactoryHelper.increment())
|
||||||
|
);
|
||||||
|
|
||||||
|
Assertions.assertEquals("Harry0", person.getName());
|
||||||
|
Assertions.assertEquals(48, person.getAge());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should lookup a constructor with a nested type definition and create nested instances")
|
||||||
|
public void nestedDefinitionsWithConstructor() {
|
||||||
|
Factory.define(Person.class, () -> new Person("Sander", FactoryHelper.randomInt(100)));
|
||||||
|
Definition<Employee> employeeDef = Factory.define(Employee.class).withConstructorArgs(Person.class);
|
||||||
|
|
||||||
|
Employee employee = employeeDef.build();
|
||||||
|
|
||||||
|
Assertions.assertEquals("Sander", employee.getPerson().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should create nested instances using only suppliers")
|
||||||
|
public void nestedDefinitionsWithoutReflection() {
|
||||||
|
Definition<Person> personDef = Factory.define(Person.class, () -> new Person("Sander", FactoryHelper.randomInt(100)));
|
||||||
|
Definition<Employee> employeeDef = Factory.define(Employee.class, () -> new Employee(personDef.build()));
|
||||||
|
|
||||||
|
Employee employee = employeeDef.build();
|
||||||
|
|
||||||
|
Assertions.assertEquals("Sander", employee.getPerson().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should create random attributes for every instance")
|
||||||
|
public void randomInts() {
|
||||||
|
Definition<Person> personDef = Factory.define(Person.class, () -> new Person("Sander", FactoryHelper.randomInt(100)));
|
||||||
|
|
||||||
|
Person person = personDef.build();
|
||||||
|
|
||||||
|
Assertions.assertEquals("Sander", person.getName());
|
||||||
|
assertTrue(person.getAge() < 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should create an object with a list of factory produced elements")
|
||||||
|
public void definitionsList() {
|
||||||
|
Factory.define(Person.class, () -> new Person("Sander" + FactoryHelper.increment(), FactoryHelper.randomInt(100)));
|
||||||
|
Factory.define(Employee.class).withConstructorArgs(Person.class);
|
||||||
|
Factory.define(Company.class).withConstructorArgs(FactoryHelper.listOf(Employee.class, Employee.class));
|
||||||
|
|
||||||
|
Company company = Factory.build(Company.class);
|
||||||
|
|
||||||
|
Assertions.assertEquals(2, company.getEmployees().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should create an object with a list of factory produced elements without reflection")
|
||||||
|
public void definitionsListWithoutReflection() {
|
||||||
|
Factory.define(Person.class, () -> new Person("Sander", 10));
|
||||||
|
Factory.define(Employee.class).withConstructorArgs(Person.class);
|
||||||
|
Factory.define(Company.class, () -> new Company(Arrays.asList(Factory.build(Employee.class), Factory.build(Employee.class))));
|
||||||
|
|
||||||
|
Company company = Factory.build(Company.class);
|
||||||
|
|
||||||
|
Assertions.assertEquals(2, company.getEmployees().size());
|
||||||
|
Assertions.assertTrue(company.getEmployees().contains(new Employee(new Person("Sander", 10))));
|
||||||
|
Assertions.assertTrue(company.getEmployees().contains(new Employee(new Person("Sander", 10))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should create a list with two elements")
|
||||||
|
public void definitionsListWithIncrementInNestedDefinition() {
|
||||||
|
Factory.define(Person.class, () -> new Person("Sander" + FactoryHelper.increment("sander"), 10));
|
||||||
|
Factory.define(Employee.class).withConstructorArgs(Person.class);
|
||||||
|
Factory.define(Company.class, () -> new Company(Arrays.asList(Factory.build(Employee.class), Factory.build(Employee.class))));
|
||||||
|
|
||||||
|
Company company = Factory.build(Company.class);
|
||||||
|
|
||||||
|
Assertions.assertEquals(2, company.getEmployees().size());
|
||||||
|
Assertions.assertTrue(company.getEmployees().contains(new Employee(new Person("Sander0", 10))));
|
||||||
|
Assertions.assertTrue(company.getEmployees().contains(new Employee(new Person("Sander1", 10))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Method withConstructor for a constructor with the wrong types should raise an exception")
|
||||||
|
public void constructorNotFound() {
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class, () ->
|
||||||
|
Factory.define(Person.class).withConstructorArgs(String.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/test/java/shautvast/edie/testdomain/Company.java
Normal file
23
src/test/java/shautvast/edie/testdomain/Company.java
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package shautvast.edie.testdomain;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Company {
|
||||||
|
private final List<Employee> employees=new ArrayList<>();
|
||||||
|
|
||||||
|
public Company(List<Employee> employees) {
|
||||||
|
this.employees.addAll(employees);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Employee> getEmployees() {
|
||||||
|
return employees;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Company{" +
|
||||||
|
"employees=" + employees +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/test/java/shautvast/edie/testdomain/Employee.java
Normal file
39
src/test/java/shautvast/edie/testdomain/Employee.java
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
package shautvast.edie.testdomain;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class Employee {
|
||||||
|
private Person person;
|
||||||
|
|
||||||
|
public Employee(Person person) {
|
||||||
|
this.person = person;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Person getPerson() {
|
||||||
|
return person;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPerson(Person person) {
|
||||||
|
this.person = person;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Employee employee = (Employee) o;
|
||||||
|
return Objects.equals(person, employee.person);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(person);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Employee{" +
|
||||||
|
"person=" + person +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/test/java/shautvast/edie/testdomain/Person.java
Normal file
51
src/test/java/shautvast/edie/testdomain/Person.java
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package shautvast.edie.testdomain;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public Person(String name, int age) {
|
||||||
|
this.name = name;
|
||||||
|
this.age = age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAge() {
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAge(int age) {
|
||||||
|
this.age = age;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Person person = (Person) o;
|
||||||
|
return age == person.age &&
|
||||||
|
Objects.equals(name, person.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(name, age);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Person{" +
|
||||||
|
"name='" + name + '\'' +
|
||||||
|
", age=" + age +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue