Skip to content

Commit f9673da

Browse files
committed
refactor: extract collection randomisation
1 parent 7ab25a4 commit f9673da

File tree

4 files changed

+38
-26
lines changed

4 files changed

+38
-26
lines changed

src/main/java/com/github/nylle/javafixture/InstanceFactory.java

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import java.util.ArrayDeque;
1616
import java.util.ArrayList;
1717
import java.util.Collection;
18-
import java.util.Collections;
1918
import java.util.Deque;
2019
import java.util.HashMap;
2120
import java.util.HashSet;
@@ -24,7 +23,6 @@
2423
import java.util.Map;
2524
import java.util.NavigableSet;
2625
import java.util.Queue;
27-
import java.util.Random;
2826
import java.util.Set;
2927
import java.util.SortedSet;
3028
import java.util.TreeSet;
@@ -37,12 +35,11 @@
3735

3836
import static java.lang.String.format;
3937
import static java.util.Arrays.stream;
40-
import static java.util.stream.Collectors.toList;
4138

4239
public class InstanceFactory {
4340

4441
private final SpecimenFactory specimenFactory;
45-
private final Random random;
42+
private final PseudoRandom random;
4643

4744
private static final Map<Class<?>, Object> primitiveDefaults = Map.of(
4845
Boolean.TYPE, false,
@@ -57,34 +54,27 @@ public class InstanceFactory {
5754

5855
public InstanceFactory(SpecimenFactory specimenFactory) {
5956
this.specimenFactory = specimenFactory;
60-
this.random = new Random();
57+
this.random = new PseudoRandom();
6158
}
6259

6360
public <T> T construct(final SpecimenType<T> type, CustomizationContext customizationContext) {
64-
var constructors = type.getDeclaredConstructors()
61+
return random.shuffled(type.getDeclaredConstructors())
6562
.stream()
6663
.filter(x -> Modifier.isPublic(x.getModifiers()))
67-
.collect(toList());
68-
69-
if (constructors.isEmpty()) {
70-
return manufacture(type, customizationContext);
71-
}
72-
73-
return construct(type, constructors.get(random.nextInt(constructors.size())), customizationContext);
64+
.findFirst()
65+
.map(x -> construct(type, x, customizationContext))
66+
.orElseGet(() -> manufacture(type, customizationContext));
7467
}
7568

7669
public <T> T manufacture(final SpecimenType<T> type, CustomizationContext customizationContext) {
77-
var factoryMethods = type.getFactoryMethods();
78-
Collections.shuffle(factoryMethods);
79-
var results = factoryMethods
70+
return random.shuffled(type.getFactoryMethods())
8071
.stream()
8172
.filter(method -> Modifier.isPublic(method.getModifiers()))
8273
.filter(method -> !hasSpecimenTypeAsParameter(method, type))
8374
.map(x -> manufactureOrNull(x, type, customizationContext))
8475
.filter(x -> x != null)
85-
.findFirst();
86-
87-
return results.orElseThrow(() -> new SpecimenException(format("Cannot manufacture %s", type.asClass())));
76+
.findFirst()
77+
.orElseThrow(() -> new SpecimenException(format("Cannot create instance of %s", type.asClass())));
8878
}
8979

9080
private <T> boolean hasSpecimenTypeAsParameter(Method m, SpecimenType<T> type) {
@@ -107,7 +97,7 @@ public <T> Object proxy(final SpecimenType<T> type) {
10797
public <T> Object proxy(final SpecimenType<T> type, final Map<String, ISpecimen<?>> specimens) {
10898
if (type.isInterface()) {
10999
var proxyFactory = new ProxyFactory();
110-
proxyFactory.setInterfaces( new Class<?>[]{type.asClass()});
100+
proxyFactory.setInterfaces(new Class<?>[]{type.asClass()});
111101
try {
112102
return proxyFactory.create(new Class[0], new Object[0], new ProxyInvocationHandler(specimenFactory, specimens));
113103
} catch (Exception e) {
@@ -227,8 +217,7 @@ private <G, T extends Collection<G>> T createCollectionFromInterfaceType(final C
227217
private <G, T extends Collection<G>> T createCollectionFromConcreteType(final SpecimenType<T> type) {
228218
try {
229219
return type.asClass().getDeclaredConstructor().newInstance();
230-
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
231-
NoSuchMethodException e) {
220+
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
232221
throw new SpecimenException("Unable to create collection of type " + type.getName(), e);
233222
}
234223
}

src/main/java/com/github/nylle/javafixture/PseudoRandom.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
import com.github.nylle.javafixture.specimen.constraints.StringConstraints;
44

55
import java.nio.charset.Charset;
6+
import java.util.ArrayList;
7+
import java.util.Collection;
8+
import java.util.Collections;
9+
import java.util.List;
610
import java.util.Random;
711
import java.util.UUID;
812
import java.util.stream.Collectors;
@@ -77,4 +81,10 @@ public Character nextChar() {
7781
public Byte nextByte() {
7882
return UUID.randomUUID().toString().getBytes(Charset.defaultCharset())[0];
7983
}
84+
85+
public <T> List<T> shuffled(Collection<T> collection) {
86+
var result = new ArrayList<>(collection);
87+
Collections.shuffle(result, random);
88+
return result;
89+
}
8090
}

src/test/java/com/github/nylle/javafixture/InstanceFactoryTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ void canOnlyUsePublicConstructor() {
9696

9797
assertThatExceptionOfType(SpecimenException.class)
9898
.isThrownBy(() -> sut.construct(fromClass(TestObjectWithPrivateConstructor.class), new CustomizationContext(List.of(), Map.of(), false)))
99-
.withMessageContaining("Cannot manufacture class")
99+
.withMessageContaining("Cannot create instance of class")
100100
.withNoCause();
101101
}
102102

@@ -111,7 +111,7 @@ void useFactoryMethodWhenNoConstructorExists() {
111111
}
112112

113113
@Test
114-
@DisplayName("will fallback to factor method when constructor fails")
114+
@DisplayName("will fallback to factory method when constructor fails")
115115
void fallbackToFactoryMethodWhenConstructorThrowsException() {
116116
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
117117

@@ -190,7 +190,7 @@ void canOnlyUsePublicFactoryMethods() {
190190

191191
assertThatExceptionOfType(SpecimenException.class)
192192
.isThrownBy(() -> sut.manufacture(fromClass(TestObjectWithNonPublicFactoryMethods.class), noContext()))
193-
.withMessageContaining("Cannot manufacture class")
193+
.withMessageContaining("Cannot create instance of class")
194194
.withNoCause();
195195
}
196196

@@ -256,7 +256,7 @@ void factoryMethodWithGenericArgument() {
256256
}
257257

258258
@Test
259-
@DisplayName("method without arguments is used")
259+
@DisplayName("generic method without arguments is used")
260260
void genericNoArgumentFactoryMethod() {
261261
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
262262

src/test/java/com/github/nylle/javafixture/PseudoRandomTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import org.junit.jupiter.api.DisplayName;
77
import org.junit.jupiter.api.Test;
88

9+
import java.util.List;
10+
911
import static org.assertj.core.api.Assertions.assertThat;
1012
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1113

@@ -84,4 +86,15 @@ void nextChar() {
8486
void nextByte() {
8587
assertThat(new PseudoRandom().nextByte()).isBetween(Byte.MIN_VALUE, Byte.MAX_VALUE);
8688
}
89+
90+
@Test
91+
void shuffled() {
92+
var input = List.of(0, 1, 2, 3, 4);
93+
94+
var actual = new PseudoRandom().shuffled(input);
95+
96+
assertThat(actual).isNotEqualTo(input);
97+
assertThat(actual).isNotSameAs(input);
98+
assertThat(actual).containsExactlyInAnyOrderElementsOf(input);
99+
}
87100
}

0 commit comments

Comments
 (0)