com.github.nylle.javafixture.InstanceFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javafixture Show documentation
Show all versions of javafixture Show documentation
JavaFixture is the attempt to bring Mark Seemann's AutoFixture for .NET to the Java world. Its purpose
is to generate full object graphs for use in test suites with a fluent API for customising the test objects
during generation.
package com.github.nylle.javafixture;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import org.objenesis.instantiator.ObjectInstantiator;
import javassist.util.proxy.ProxyFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
import static java.lang.String.format;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
public class InstanceFactory {
private final SpecimenFactory specimenFactory;
private final Random random;
private static final Map, Object> primitiveDefaults = Map.of(
Boolean.TYPE, false,
Character.TYPE, '\0',
Byte.TYPE, (byte) 0,
Short.TYPE, 0,
Integer.TYPE, 0,
Long.TYPE, 0L,
Float.TYPE, 0.0f,
Double.TYPE, 0.0d
);
public InstanceFactory(SpecimenFactory specimenFactory) {
this.specimenFactory = specimenFactory;
this.random = new Random();
}
public T construct(final SpecimenType type, CustomizationContext customizationContext) {
var constructors = type.getDeclaredConstructors()
.stream()
.filter(x -> Modifier.isPublic(x.getModifiers()))
.collect(toList());
if (constructors.isEmpty()) {
return manufacture(type, customizationContext);
}
return construct(type, constructors.get(random.nextInt(constructors.size())), customizationContext);
}
public T manufacture(final SpecimenType type, CustomizationContext customizationContext) {
var factoryMethods = type.getFactoryMethods();
Collections.shuffle(factoryMethods);
var results = factoryMethods
.stream()
.filter(method -> Modifier.isPublic(method.getModifiers()))
.filter(method -> !hasSpecimenTypeAsParameter(method, type))
.map(x -> manufactureOrNull(x, type, customizationContext))
.filter(x -> x != null)
.findFirst();
return results.orElseThrow(() -> new SpecimenException(format("Cannot manufacture %s", type.asClass())));
}
private boolean hasSpecimenTypeAsParameter(Method m, SpecimenType type) {
return stream(m.getGenericParameterTypes())
.anyMatch(t -> t.getTypeName().equals(type.asClass().getName()));
}
public T instantiate(final SpecimenType type) {
try {
return type.asClass().getDeclaredConstructor().newInstance();
} catch (Exception e) {
return (T) ((ObjectInstantiator) ((Objenesis) new ObjenesisStd()).getInstantiatorOf(type.asClass())).newInstance();
}
}
public Object proxy(final SpecimenType type) {
return proxy(type, new HashMap<>());
}
public Object proxy(final SpecimenType type, final Map> specimens) {
if (type.isInterface()) {
return Proxy.newProxyInstance(
type.asClass().getClassLoader(),
new Class[]{type.asClass()},
new ProxyInvocationHandler(specimenFactory, specimens));
}
return createProxyForAbstract(type, specimens);
}
public > T createCollection(final SpecimenType type) {
return type.isInterface() ? createCollectionFromInterfaceType(type.asClass()) : createCollectionFromConcreteType(type);
}
private T construct(final SpecimenType type, final Constructor> constructor, CustomizationContext customizationContext) {
try {
constructor.setAccessible(true);
return (T) constructor.newInstance(stream(constructor.getParameters())
.map(p -> createParameter(p, customizationContext))
.toArray());
} catch (Exception e) {
return manufacture(type, customizationContext);
}
}
private static Object defaultValue(Class> type) {
if (type.isPrimitive()) {
return primitiveDefaults.get(type);
} else {
return null;
}
}
private Object createParameter(Parameter parameter, CustomizationContext customizationContext) {
if (customizationContext.getIgnoredFields().contains(parameter.getName())) {
return defaultValue(parameter.getType());
}
if (customizationContext.getCustomFields().containsKey(parameter.getName())) {
return customizationContext.getCustomFields().get(parameter.getName());
}
var specimen = specimenFactory.build(SpecimenType.fromClass(parameter.getParameterizedType()));
return specimen.create(new CustomizationContext(List.of(), Map.of(), customizationContext.useRandomConstructor()), new Annotation[0]);
}
private T createProxyForAbstract(final SpecimenType type, final Map> specimens) {
try {
var factory = new ProxyFactory();
factory.setSuperclass(type.asClass());
return (T) factory.create(new Class>[0], new Object[0], new ProxyInvocationHandler(specimenFactory, specimens));
} catch (Exception e) {
throw new SpecimenException(format("Unable to construct abstract class %s: %s", type.asClass(), e.getMessage()), e);
}
}
private T manufactureOrNull(final Method method, SpecimenType type, CustomizationContext customizationContext) {
try {
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy