Skip to content

Commit 614a9ff

Browse files
author
Piotr Joñski
committed
#84. Partial implementation for choosing constructor.
1 parent 2f539da commit 614a9ff

16 files changed

+367
-27
lines changed

gradle/wrapper/gradle-wrapper.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#Wed Aug 24 10:04:35 CEST 2016
1+
#Mon Sep 19 19:36:03 CEST 2016
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME

src/main/java/pl/pojo/tester/api/AbstractTester.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package pl.pojo.tester.api;
22

33
import java.util.Arrays;
4+
import java.util.HashMap;
45
import java.util.List;
6+
import java.util.Map;
57
import java.util.function.Predicate;
68
import org.apache.commons.lang3.builder.EqualsBuilder;
79
import org.apache.commons.lang3.builder.HashCodeBuilder;
@@ -15,13 +17,15 @@ public abstract class AbstractTester {
1517

1618
protected ObjectGenerator objectGenerator;
1719
protected TestAssertions testAssertions = new TestAssertions();
20+
private Map<Class<?>, Object[]> classAndConstructorParameters = new HashMap<>();
21+
private AbstractFieldValueChanger fieldValuesChanger = DefaultFieldValueChanger.INSTANCE;
1822

1923
public AbstractTester() {
2024
this(DefaultFieldValueChanger.INSTANCE);
2125
}
2226

2327
public AbstractTester(final AbstractFieldValueChanger abstractFieldValueChanger) {
24-
objectGenerator = new ObjectGenerator(abstractFieldValueChanger);
28+
objectGenerator = new ObjectGenerator(abstractFieldValueChanger, classAndConstructorParameters);
2529
}
2630

2731
public void test(final Class<?> clazz) {
@@ -50,7 +54,13 @@ public void testAll(final ClassAndFieldPredicatePair... classesAndFieldPredicate
5054
public abstract void test(final ClassAndFieldPredicatePair baseClassAndFieldPredicatePair, final ClassAndFieldPredicatePair... classAndFieldPredicatePairs);
5155

5256
public void setFieldValuesChanger(final AbstractFieldValueChanger fieldValuesChanger) {
53-
objectGenerator = new ObjectGenerator(fieldValuesChanger);
57+
this.fieldValuesChanger = fieldValuesChanger;
58+
objectGenerator = new ObjectGenerator(fieldValuesChanger, classAndConstructorParameters);
59+
}
60+
61+
public void setUserDefinedConstructors(final Map<Class<?>, Object[]> classAndConstructorParameters) {
62+
this.classAndConstructorParameters = classAndConstructorParameters;
63+
objectGenerator = new ObjectGenerator(fieldValuesChanger, classAndConstructorParameters);
5464
}
5565

5666
@Override
@@ -67,13 +77,17 @@ public boolean equals(final Object o) {
6777

6878
return new EqualsBuilder().append(objectGenerator, that.objectGenerator)
6979
.append(testAssertions, that.testAssertions)
80+
.append(classAndConstructorParameters, that.classAndConstructorParameters)
81+
.append(fieldValuesChanger, that.fieldValuesChanger)
7082
.isEquals();
7183
}
7284

7385
@Override
7486
public int hashCode() {
7587
return new HashCodeBuilder().append(objectGenerator)
7688
.append(testAssertions)
89+
.append(classAndConstructorParameters)
90+
.append(fieldValuesChanger)
7791
.toHashCode();
7892
}
7993
}

src/main/java/pl/pojo/tester/api/assertion/AbstractAssetion.java

+10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package pl.pojo.tester.api.assertion;
22

33
import java.util.Arrays;
4+
import java.util.HashMap;
45
import java.util.HashSet;
6+
import java.util.Map;
57
import java.util.Set;
68
import lombok.AccessLevel;
79
import lombok.NoArgsConstructor;
@@ -20,6 +22,7 @@ public abstract class AbstractAssetion {
2022
.forEach(DEFAULT_TESTERS::add);
2123
}
2224

25+
private final Map<Class<?>, Object[]> classAndConstructorParameters = new HashMap<>();
2326
Set<AbstractTester> testers = new HashSet<>();
2427
private AbstractFieldValueChanger abstractFieldValueChanger;
2528

@@ -48,9 +51,16 @@ public void areWellImplemented() {
4851
testers.forEach(tester -> tester.setFieldValuesChanger(abstractFieldValueChanger));
4952
}
5053

54+
testers.forEach(tester -> tester.setUserDefinedConstructors(classAndConstructorParameters));
55+
5156
testImplementation();
5257
}
5358

59+
public AbstractAssetion create(final Class<?> clazz, final Object... constructorParameters) {
60+
classAndConstructorParameters.put(clazz, constructorParameters);
61+
return this;
62+
}
63+
5464
protected abstract void testImplementation();
5565

5666
}

src/main/java/pl/pojo/tester/internal/instantiator/BestConstructorInstantiator.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
import java.lang.reflect.Constructor;
55
import java.lang.reflect.InvocationTargetException;
66
import java.util.Arrays;
7+
import java.util.Map;
78

89
class BestConstructorInstantiator extends ObjectInstantiator {
910

10-
BestConstructorInstantiator(final Class<?> clazz) {
11+
private final Map<Class<?>, Object[]> classAndConstructorParameters;
12+
13+
BestConstructorInstantiator(final Class<?> clazz, final Map<Class<?>, Object[]> classAndConstructorParameters) {
1114
super(clazz);
15+
this.classAndConstructorParameters = classAndConstructorParameters;
1216
}
1317

1418
@Override
@@ -45,7 +49,7 @@ private Object createObjectFromConstructor(final Constructor<?> constructor) {
4549
}
4650

4751
private Object instantiate(final Class<?> clazz) {
48-
return Instantiable.forClass(clazz)
52+
return Instantiable.forClass(clazz, classAndConstructorParameters)
4953
.instantiate();
5054
}
5155

src/main/java/pl/pojo/tester/internal/instantiator/Instantiable.java

+13-4
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,29 @@
44
import java.lang.reflect.Constructor;
55
import java.lang.reflect.Modifier;
66
import java.util.Arrays;
7+
import java.util.Map;
78

89
public abstract class Instantiable {
910

10-
public static ObjectInstantiator forClass(final String qualifiedClassName) {
11+
public static ObjectInstantiator forClass(final String qualifiedClassName, final Map<Class<?>, Object[]> classAndConstructorParameters) {
1112
final Class<?> clazz;
1213
try {
1314
clazz = Class.forName(qualifiedClassName);
1415
} catch (final ClassNotFoundException e) {
1516
throw new ObjectInstantiationException(qualifiedClassName, e);
1617
}
17-
return forClass(clazz);
18+
return forClass(clazz, classAndConstructorParameters);
1819
}
1920

20-
public static ObjectInstantiator forClass(final Class<?> clazz) {
21+
static ObjectInstantiator forClass(final Class<?> clazz, final Map<Class<?>, Object[]> classAndConstructorParameters) {
22+
if (userDefinedConstructorParametersFor(clazz, classAndConstructorParameters)) {
23+
return new UserDefinedConstructorInstantiator(clazz, classAndConstructorParameters);
24+
}
25+
2126
if (isStringClass(clazz)) {
2227
return new StringClassInstantiator();
2328
}
29+
2430
if (canBeCreatedByDefaultConstructor(clazz)) {
2531
return new DefaultConstructorInstantiator(clazz);
2632
}
@@ -41,8 +47,11 @@ public static ObjectInstantiator forClass(final Class<?> clazz) {
4147
return new ProxyInstantiator(clazz);
4248
}
4349

44-
return new BestConstructorInstantiator(clazz);
50+
return new BestConstructorInstantiator(clazz, classAndConstructorParameters);
51+
}
4552

53+
private static boolean userDefinedConstructorParametersFor(final Class<?> clazz, final Map<Class<?>, Object[]> classAndConstructorParameters) {
54+
return classAndConstructorParameters.containsKey(clazz);
4655
}
4756

4857
private static boolean isStringClass(final Class<?> clazz) {

src/main/java/pl/pojo/tester/internal/instantiator/ObjectGenerator.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919
public class ObjectGenerator {
2020

2121
private final AbstractFieldValueChanger abstractFieldValueChanger;
22+
private final Map<Class<?>, Object[]> classAndConstructorParameters;
2223

23-
public ObjectGenerator(final AbstractFieldValueChanger abstractFieldValueChanger) {
24+
public ObjectGenerator(final AbstractFieldValueChanger abstractFieldValueChanger,
25+
final Map<Class<?>, Object[]> classAndConstructorParameters) {
2426
this.abstractFieldValueChanger = abstractFieldValueChanger;
27+
this.classAndConstructorParameters = classAndConstructorParameters;
2528
}
2629

2730
public Object createNewInstance(final Class<?> clazz) {
28-
return Instantiable.forClass(clazz)
31+
return Instantiable.forClass(clazz, classAndConstructorParameters)
2932
.instantiate();
3033
}
3134

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package pl.pojo.tester.internal.instantiator;
2+
3+
import java.lang.reflect.Constructor;
4+
import java.lang.reflect.InvocationTargetException;
5+
import java.lang.reflect.Modifier;
6+
import java.util.Arrays;
7+
import java.util.Map;
8+
import java.util.stream.Stream;
9+
10+
class UserDefinedConstructorInstantiator extends ObjectInstantiator {
11+
12+
private final Map<Class<?>, Object[]> classAndConstructorParameters;
13+
14+
UserDefinedConstructorInstantiator(final Class<?> clazz, final Map<Class<?>, Object[]> classAndConstructorParameters) {
15+
super(clazz);
16+
this.classAndConstructorParameters = classAndConstructorParameters;
17+
}
18+
19+
@Override
20+
public Object instantiate() {
21+
try {
22+
final Object[] constructorParameters = classAndConstructorParameters.get(clazz);
23+
Class[] constructorParametersTypes = convertToParameterTypes(constructorParameters);
24+
Object[] arguments = constructorParameters;
25+
26+
if (isInnerClass()) {
27+
constructorParametersTypes = putEnclosingClassAsFirstParameterType(clazz.getEnclosingClass(), constructorParameters);
28+
final Object enclosingClassInstance = instantiateEnclosingClass();
29+
arguments = putEnclosingClassInstanceAsFirstParameter(enclosingClassInstance, arguments);
30+
}
31+
32+
final Constructor<?> constructor = clazz.getDeclaredConstructor(constructorParametersTypes);
33+
constructor.setAccessible(true);
34+
return constructor.newInstance(arguments);
35+
} catch (final NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException | IllegalArgumentException e) {
36+
throw new ObjectInstantiationException(clazz, e);
37+
}
38+
}
39+
40+
private Object instantiateEnclosingClass() {
41+
final Class<?> enclosingClass = clazz.getEnclosingClass();
42+
return Instantiable.forClass(enclosingClass, classAndConstructorParameters)
43+
.instantiate();
44+
}
45+
46+
private Object[] putEnclosingClassInstanceAsFirstParameter(final Object enclosingClassInstance, final Object[] arguments) {
47+
return Stream.concat(Stream.of(enclosingClassInstance), Arrays.stream(arguments))
48+
.toArray(Object[]::new);
49+
}
50+
51+
52+
private Class[] putEnclosingClassAsFirstParameterType(final Class<?> enclosingClass, final Object[] constructorParameters) {
53+
final Class[] parameterTypes = convertToParameterTypes(constructorParameters);
54+
return Stream.concat(Stream.of(enclosingClass), Arrays.stream(parameterTypes))
55+
.toArray(Class[]::new);
56+
}
57+
58+
private boolean isInnerClass() {
59+
return clazz.getEnclosingClass() != null && !Modifier.isStatic(clazz.getModifiers());
60+
}
61+
62+
private Class[] convertToParameterTypes(final Object[] constructorParameters) {
63+
return Arrays.stream(constructorParameters)
64+
.map(Object::getClass)
65+
.toArray(Class[]::new);
66+
}
67+
}
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package matchers;
2+
3+
import java.util.Arrays;
4+
import java.util.Map;
5+
import org.mockito.ArgumentMatcher;
6+
7+
public class MapMatcher extends ArgumentMatcher<Map<Class<?>, Object[]>> {
8+
9+
private final Class<?> expectedClass;
10+
private final Object[] expectedArguments;
11+
12+
public MapMatcher(final Class<?> expectedClass, final Object[] expectedArguments) {
13+
this.expectedClass = expectedClass;
14+
this.expectedArguments = expectedArguments;
15+
}
16+
17+
@Override
18+
public boolean matches(final Object argument) {
19+
final Map<Class, Object[]> actualMap = (Map<Class, Object[]>) argument;
20+
if (!actualMap.containsKey(expectedClass)) {
21+
return false;
22+
}
23+
final Object[] actualArguments = actualMap.get(expectedClass);
24+
return Arrays.equals(actualArguments, expectedArguments);
25+
}
26+
}

src/test/java/pl/pojo/tester/api/AbstractTesterTest.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package pl.pojo.tester.api;
22

3+
import java.util.HashMap;
34
import java.util.function.Predicate;
45
import lombok.Data;
56
import matchers.ClassAndFieldPredicatePairArgumentMatcher;
@@ -87,7 +88,7 @@ public void Should_Call_Test_With_Expected_Class_And_Field_Predicate_Pairs_Two_T
8788
}
8889

8990
@Test
90-
public void Should_Create_New_Object_Generator() {
91+
public void Should_Create_New_Object_Generator_When_Set_Field_Value_Changer() {
9192
// given
9293
final AbstractTester abstractTester = new AbstractTesterImplementation();
9394
final AbstractFieldValueChanger fieldValuesChanger = DefaultFieldValueChanger.INSTANCE;
@@ -101,6 +102,20 @@ public void Should_Create_New_Object_Generator() {
101102
assertThat(beforeChange).isNotEqualTo(afterChange);
102103
}
103104

105+
@Test
106+
public void Should_Create_New_Object_Generator_When_User_Defined_Class_And_Constructor() {
107+
// given
108+
final AbstractTester abstractTester = new AbstractTesterImplementation();
109+
final ObjectGenerator beforeChange = Whitebox.getInternalState(abstractTester, "objectGenerator");
110+
111+
// when
112+
abstractTester.setUserDefinedConstructors(new HashMap<>());
113+
final ObjectGenerator afterChange = Whitebox.getInternalState(abstractTester, "objectGenerator");
114+
115+
// then
116+
assertThat(beforeChange).isNotEqualTo(afterChange);
117+
}
118+
104119
@Test
105120
public void Should_Equal_Itself() {
106121
// given

src/test/java/pl/pojo/tester/api/assertion/AbstractAssetionTest.java

+19-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import classesForTest.GoodPojo_Equals_HashCode_ToString;
44
import classesForTest.equals.BadPojoEqualsItself;
55
import com.google.common.collect.Sets;
6+
import matchers.MapMatcher;
67
import org.junit.jupiter.api.Test;
78
import org.junit.platform.runner.JUnitPlatform;
89
import org.junit.runner.RunWith;
@@ -103,7 +104,24 @@ public void Should_Set_Field_Value_Changer_To_Testers() {
103104
abstractAssetion.areWellImplemented();
104105

105106
// then
106-
verify(equalsTester, only()).setFieldValuesChanger(expectedFieldsValuesChanger);
107+
verify(equalsTester, times(1)).setFieldValuesChanger(expectedFieldsValuesChanger);
108+
}
109+
110+
@Test
111+
public void Should_Set_User_Defined_Class_And_Constructor_Paramters_To_Tester() {
112+
// given
113+
final AbstractAssetion abstractAssetion = new AbstractAssetionImplementation();
114+
final EqualsTester equalsTester = mock(EqualsTester.class);
115+
Whitebox.setInternalState(abstractAssetion, "testers", Sets.newHashSet(equalsTester));
116+
final Class<String> expectedClass = String.class;
117+
final Object[] expectedArguments = {'c', 'h', 'a', 'r'};
118+
abstractAssetion.create(expectedClass, expectedArguments);
119+
120+
// when
121+
abstractAssetion.areWellImplemented();
122+
123+
// then
124+
verify(equalsTester, times(1)).setUserDefinedConstructors(argThat(new MapMatcher(expectedClass, expectedArguments)));
107125
}
108126

109127
private class AbstractAssetionImplementation extends AbstractAssetion {

0 commit comments

Comments
 (0)