Skip to content

Commit 8d8ca11

Browse files
author
Piotr Joński
authored
Merge pull request #97 from sta-szek/#84-Allow-user-to-choose-constructor-and-pass-parameters
#84. Partial implementation for choosing constructor.
2 parents 2f539da + 9de41d5 commit 8d8ca11

17 files changed

+392
-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<?>, ConstructorParameters> constructorParameters = 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, constructorParameters);
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, constructorParameters);
59+
}
60+
61+
public void setUserDefinedConstructors(final Map<Class<?>, ConstructorParameters> constructorParameters) {
62+
this.constructorParameters = constructorParameters;
63+
objectGenerator = new ObjectGenerator(fieldValuesChanger, constructorParameters);
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(constructorParameters, that.constructorParameters)
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(constructorParameters)
90+
.append(fieldValuesChanger)
7791
.toHashCode();
7892
}
7993
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package pl.pojo.tester.api;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
@AllArgsConstructor
7+
@Getter
8+
public class ConstructorParameters {
9+
10+
private Object[] constructorParameters;
11+
private Class<?>[] constructorParametersTypes;
12+
}

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

+16
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
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;
810
import pl.pojo.tester.api.AbstractTester;
11+
import pl.pojo.tester.api.ConstructorParameters;
912
import pl.pojo.tester.internal.field.AbstractFieldValueChanger;
1013

1114
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@@ -20,6 +23,7 @@ public abstract class AbstractAssetion {
2023
.forEach(DEFAULT_TESTERS::add);
2124
}
2225

26+
private final Map<Class<?>, ConstructorParameters> constructorParameters = new HashMap<>();
2327
Set<AbstractTester> testers = new HashSet<>();
2428
private AbstractFieldValueChanger abstractFieldValueChanger;
2529

@@ -48,9 +52,21 @@ public void areWellImplemented() {
4852
testers.forEach(tester -> tester.setFieldValuesChanger(abstractFieldValueChanger));
4953
}
5054

55+
testers.forEach(tester -> tester.setUserDefinedConstructors(constructorParameters));
56+
5157
testImplementation();
5258
}
5359

60+
public AbstractAssetion create(final Class<?> clazz, final Object[] constructorParameters, final Class<?>[] constructorParameterTypes) {
61+
final ConstructorParameters constructorParameter = new ConstructorParameters(constructorParameters, constructorParameterTypes);
62+
return create(clazz, constructorParameter);
63+
}
64+
65+
public AbstractAssetion create(final Class<?> clazz, final ConstructorParameters constructorParameters) {
66+
this.constructorParameters.put(clazz, constructorParameters);
67+
return this;
68+
}
69+
5470
protected abstract void testImplementation();
5571

5672
}

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44
import java.lang.reflect.Constructor;
55
import java.lang.reflect.InvocationTargetException;
66
import java.util.Arrays;
7+
import java.util.Map;
8+
import pl.pojo.tester.api.ConstructorParameters;
79

810
class BestConstructorInstantiator extends ObjectInstantiator {
911

10-
BestConstructorInstantiator(final Class<?> clazz) {
12+
private final Map<Class<?>, ConstructorParameters> constructorParameters;
13+
14+
BestConstructorInstantiator(final Class<?> clazz, final Map<Class<?>, ConstructorParameters> constructorParameters) {
1115
super(clazz);
16+
this.constructorParameters = constructorParameters;
1217
}
1318

1419
@Override
@@ -45,7 +50,7 @@ private Object createObjectFromConstructor(final Constructor<?> constructor) {
4550
}
4651

4752
private Object instantiate(final Class<?> clazz) {
48-
return Instantiable.forClass(clazz)
53+
return Instantiable.forClass(clazz, constructorParameters)
4954
.instantiate();
5055
}
5156

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

+14-4
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,30 @@
44
import java.lang.reflect.Constructor;
55
import java.lang.reflect.Modifier;
66
import java.util.Arrays;
7+
import java.util.Map;
8+
import pl.pojo.tester.api.ConstructorParameters;
79

810
public abstract class Instantiable {
911

10-
public static ObjectInstantiator forClass(final String qualifiedClassName) {
12+
public static ObjectInstantiator forClass(final String qualifiedClassName, final Map<Class<?>, ConstructorParameters> constructorParameters) {
1113
final Class<?> clazz;
1214
try {
1315
clazz = Class.forName(qualifiedClassName);
1416
} catch (final ClassNotFoundException e) {
1517
throw new ObjectInstantiationException(qualifiedClassName, e);
1618
}
17-
return forClass(clazz);
19+
return forClass(clazz, constructorParameters);
1820
}
1921

20-
public static ObjectInstantiator forClass(final Class<?> clazz) {
22+
static ObjectInstantiator forClass(final Class<?> clazz, final Map<Class<?>, ConstructorParameters> constructorParameters) {
23+
if (userDefinedConstructorParametersFor(clazz, constructorParameters)) {
24+
return new UserDefinedConstructorInstantiator(clazz, constructorParameters);
25+
}
26+
2127
if (isStringClass(clazz)) {
2228
return new StringClassInstantiator();
2329
}
30+
2431
if (canBeCreatedByDefaultConstructor(clazz)) {
2532
return new DefaultConstructorInstantiator(clazz);
2633
}
@@ -41,8 +48,11 @@ public static ObjectInstantiator forClass(final Class<?> clazz) {
4148
return new ProxyInstantiator(clazz);
4249
}
4350

44-
return new BestConstructorInstantiator(clazz);
51+
return new BestConstructorInstantiator(clazz, constructorParameters);
52+
}
4553

54+
private static boolean userDefinedConstructorParametersFor(final Class<?> clazz, final Map<Class<?>, ConstructorParameters> constructorParameters) {
55+
return constructorParameters.containsKey(clazz);
4656
}
4757

4858
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
@@ -13,19 +13,22 @@
1313
import java.util.stream.IntStream;
1414
import java.util.stream.Stream;
1515
import pl.pojo.tester.api.ClassAndFieldPredicatePair;
16+
import pl.pojo.tester.api.ConstructorParameters;
1617
import pl.pojo.tester.internal.field.AbstractFieldValueChanger;
1718
import pl.pojo.tester.internal.utils.FieldUtils;
1819

1920
public class ObjectGenerator {
2021

2122
private final AbstractFieldValueChanger abstractFieldValueChanger;
23+
private final Map<Class<?>, ConstructorParameters> constructorParameters;
2224

23-
public ObjectGenerator(final AbstractFieldValueChanger abstractFieldValueChanger) {
25+
public ObjectGenerator(final AbstractFieldValueChanger abstractFieldValueChanger, final Map<Class<?>, ConstructorParameters> constructorParameters) {
2426
this.abstractFieldValueChanger = abstractFieldValueChanger;
27+
this.constructorParameters = constructorParameters;
2528
}
2629

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

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

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

+22-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
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;
910
import org.powermock.reflect.Whitebox;
11+
import pl.pojo.tester.api.ConstructorParameters;
1012
import pl.pojo.tester.api.EqualsTester;
1113
import pl.pojo.tester.api.HashCodeTester;
1214
import pl.pojo.tester.internal.assertion.AssertionError;
@@ -103,7 +105,26 @@ public void Should_Set_Field_Value_Changer_To_Testers() {
103105
abstractAssetion.areWellImplemented();
104106

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

109130
private class AbstractAssetionImplementation extends AbstractAssetion {

0 commit comments

Comments
 (0)