Skip to content

Commit 96445f1

Browse files
Nylleakutschera
authored andcommitted
feat: pass exceptions on to manufacture
Refs: #106
1 parent 9eebb6d commit 96445f1

File tree

6 files changed

+86
-23
lines changed

6 files changed

+86
-23
lines changed

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,27 +57,27 @@ public InstanceFactory(SpecimenFactory specimenFactory) {
5757
this.random = new PseudoRandom();
5858
}
5959

60-
public <T> T construct(final SpecimenType<T> type, CustomizationContext customizationContext) {
60+
public <T> T construct(SpecimenType<T> type, CustomizationContext customizationContext) {
6161
return random.shuffled(type.getDeclaredConstructors())
6262
.stream()
6363
.filter(x -> Modifier.isPublic(x.getModifiers()))
6464
.findFirst()
6565
.map(x -> construct(type, x, customizationContext))
66-
.orElseGet(() -> manufacture(type, customizationContext));
66+
.orElseGet(() -> manufacture(type, customizationContext, new SpecimenException("No public constructor found")));
6767
}
6868

69-
public <T> T manufacture(final SpecimenType<T> type, CustomizationContext customizationContext) {
69+
public <T> T manufacture(SpecimenType<T> type, CustomizationContext customizationContext, Throwable throwable) {
7070
return random.shuffled(type.getFactoryMethods())
7171
.stream()
7272
.filter(method -> Modifier.isPublic(method.getModifiers()))
7373
.filter(method -> !hasSpecimenTypeAsParameter(method, type))
7474
.map(x -> manufactureOrNull(x, type, customizationContext))
7575
.filter(x -> x != null)
7676
.findFirst()
77-
.orElseThrow(() -> new SpecimenException(format("Cannot create instance of %s", type.asClass())));
77+
.orElseThrow(() -> new SpecimenException(format("Cannot create instance of %s: no factory-method found", type.asClass()), throwable));
7878
}
7979

80-
public <T> T instantiate(final SpecimenType<T> type) {
80+
public <T> T instantiate(SpecimenType<T> type) {
8181
try {
8282
return type.asClass().getDeclaredConstructor().newInstance();
8383
} catch (Exception e) {
@@ -111,8 +111,8 @@ private <T> T construct(final SpecimenType<T> type, final Constructor<?> constru
111111
return (T) constructor.newInstance(stream(constructor.getParameters())
112112
.map(p -> createParameter(p, customizationContext))
113113
.toArray());
114-
} catch (Exception e) {
115-
return manufacture(type, customizationContext);
114+
} catch (Exception ex) {
115+
return manufacture(type, customizationContext, ex);
116116
}
117117
}
118118

@@ -149,8 +149,8 @@ private <T> T createProxyForAbstract(final SpecimenType<T> type, final Map<Strin
149149
var factory = new ProxyFactory();
150150
factory.setSuperclass(type.asClass());
151151
return (T) factory.create(new Class<?>[0], new Object[0], new ProxyInvocationHandler(specimenFactory, specimens));
152-
} catch (Exception e) {
153-
throw new SpecimenException(format("Unable to create instance of abstract class %s: %s", type.asClass(), e.getMessage()), e);
152+
} catch (Exception ex) {
153+
throw new SpecimenException(format("Unable to create instance of abstract %s: %s", type.asClass(), ex.getMessage()), ex);
154154
}
155155
}
156156

src/main/java/com/github/nylle/javafixture/specimen/AbstractSpecimen.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public T create(final CustomizationContext customizationContext, Annotation[] an
6262
field.getGenericType().getTypeName(),
6363
specimenFactory.build(SpecimenType.fromClass(field.getGenericType()))).create(new CustomizationContext(List.of(), Map.of(), false), field.getAnnotations()))));
6464
return context.remove(type);
65-
} catch(SpecimenException ignored) {
66-
return instanceFactory.manufacture(type, customizationContext);
65+
} catch(SpecimenException ex) {
66+
return instanceFactory.manufacture(type, customizationContext, ex);
6767
}
6868
}
6969
}

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

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
import com.github.nylle.javafixture.testobjects.factorymethod.GenericClassWithFactoryMethodWithoutArgument;
1212
import com.github.nylle.javafixture.testobjects.factorymethod.TestObjectWithNonPublicFactoryMethods;
1313
import com.github.nylle.javafixture.testobjects.interfaces.InterfaceWithDefaultMethod;
14+
import com.github.nylle.javafixture.testobjects.withconstructor.ConstructorExceptionAndNoFactoryMethod;
1415
import com.github.nylle.javafixture.testobjects.withconstructor.TestObjectWithConstructedField;
1516
import com.github.nylle.javafixture.testobjects.withconstructor.TestObjectWithGenericConstructor;
1617
import com.github.nylle.javafixture.testobjects.withconstructor.TestObjectWithPrivateConstructor;
1718
import org.junit.jupiter.api.DisplayName;
1819
import org.junit.jupiter.api.Nested;
1920
import org.junit.jupiter.api.Test;
2021

22+
import java.lang.reflect.InvocationTargetException;
2123
import java.nio.charset.Charset;
2224
import java.util.ArrayDeque;
2325
import java.util.ArrayList;
@@ -97,7 +99,9 @@ void canOnlyUsePublicConstructor() {
9799
assertThatExceptionOfType(SpecimenException.class)
98100
.isThrownBy(() -> sut.construct(fromClass(TestObjectWithPrivateConstructor.class), new CustomizationContext(List.of(), Map.of(), false)))
99101
.withMessageContaining("Cannot create instance of class")
100-
.withNoCause();
102+
.havingCause()
103+
.isInstanceOf(SpecimenException.class)
104+
.withMessage("No public constructor found");
101105
}
102106

103107
@Test
@@ -120,6 +124,17 @@ void fallbackToFactoryMethodWhenConstructorThrowsException() {
120124
assertThat(result.getValue()).isNotNull();
121125
}
122126

127+
@Test
128+
@DisplayName("will fallback to factory method and pass exceptions on")
129+
void passExceptionToFallbackWhenConstructorThrows() {
130+
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
131+
132+
assertThatExceptionOfType(SpecimenException.class)
133+
.isThrownBy(() -> sut.construct(new SpecimenType<ConstructorExceptionAndNoFactoryMethod>() {}, new CustomizationContext(List.of(), Map.of(), false)))
134+
.withMessageContaining("Cannot create instance of class")
135+
.withCauseInstanceOf(InvocationTargetException.class);
136+
}
137+
123138
@Test
124139
@DisplayName("arguments can be customized")
125140
void argumentsCanBeCustomized() {
@@ -178,7 +193,7 @@ class FactoryMethods {
178193
void canCreateInstanceFromAbstractClassUsingFactoryMethod() {
179194
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
180195

181-
var actual = sut.manufacture(new SpecimenType<Charset>() {}, noContext());
196+
var actual = sut.manufacture(new SpecimenType<Charset>() {}, noContext(), null);
182197

183198
assertThat(actual).isInstanceOf(Charset.class);
184199
}
@@ -189,17 +204,30 @@ void canOnlyUsePublicFactoryMethods() {
189204
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
190205

191206
assertThatExceptionOfType(SpecimenException.class)
192-
.isThrownBy(() -> sut.manufacture(fromClass(TestObjectWithNonPublicFactoryMethods.class), noContext()))
207+
.isThrownBy(() -> sut.manufacture(fromClass(TestObjectWithNonPublicFactoryMethods.class), noContext(), null))
193208
.withMessageContaining("Cannot create instance of class")
194209
.withNoCause();
195210
}
196211

212+
@Test
213+
@DisplayName("includes provided throwable as cause")
214+
void takesThrowableAsCause() {
215+
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
216+
217+
var throwable = new RuntimeException("expected for test");
218+
219+
assertThatExceptionOfType(SpecimenException.class)
220+
.isThrownBy(() -> sut.manufacture(fromClass(TestObjectWithNonPublicFactoryMethods.class), noContext(), throwable))
221+
.withMessageContaining("Cannot create instance of class")
222+
.withCause(throwable);
223+
}
224+
197225
@Test
198226
@DisplayName("method arguments are used")
199227
void factoryMethodWithArgument() {
200228
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
201229

202-
FactoryMethodWithArgument result = sut.manufacture(fromClass(FactoryMethodWithArgument.class), noContext());
230+
FactoryMethodWithArgument result = sut.manufacture(fromClass(FactoryMethodWithArgument.class), noContext(), null);
203231

204232
assertThat(result.getValue()).isNotNull();
205233
}
@@ -209,7 +237,7 @@ void factoryMethodWithArgument() {
209237
void shouldFilter() {
210238
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
211239

212-
FactoryMethodWithItselfAsArgument result = sut.manufacture(fromClass(FactoryMethodWithItselfAsArgument.class), noContext());
240+
FactoryMethodWithItselfAsArgument result = sut.manufacture(fromClass(FactoryMethodWithItselfAsArgument.class), noContext(), null);
213241

214242
assertThat(result.getValue()).isNull();
215243
}
@@ -220,7 +248,7 @@ void shouldFailWithoutValidFactoryMethod() {
220248
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
221249

222250
assertThatExceptionOfType(SpecimenException.class)
223-
.isThrownBy(() -> sut.manufacture(fromClass(FactoryMethodWithOnlyItselfAsArgument.class), noContext()));
251+
.isThrownBy(() -> sut.manufacture(fromClass(FactoryMethodWithOnlyItselfAsArgument.class), noContext(), null));
224252

225253
}
226254

@@ -229,7 +257,7 @@ void shouldFailWithoutValidFactoryMethod() {
229257
void createOptional() {
230258
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
231259

232-
var result = sut.manufacture(new SpecimenType<Optional<String>>() {}, noContext());
260+
var result = sut.manufacture(new SpecimenType<Optional<String>>() {}, noContext(), null);
233261

234262
assertThat(result).isInstanceOf(Optional.class);
235263
assertThat(result.orElse("optional may be empty")).isInstanceOf(String.class);
@@ -240,7 +268,7 @@ void createOptional() {
240268
void factoryMethodWithoutArgument() {
241269
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
242270

243-
FactoryMethodWithoutArgument result = sut.manufacture(fromClass(FactoryMethodWithoutArgument.class), noContext());
271+
FactoryMethodWithoutArgument result = sut.manufacture(fromClass(FactoryMethodWithoutArgument.class), noContext(), null);
244272

245273
assertThat(result.getValue()).isEqualTo(42);
246274
}
@@ -250,7 +278,7 @@ void factoryMethodWithoutArgument() {
250278
void factoryMethodWithGenericArgument() {
251279
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
252280

253-
var result = sut.manufacture(new SpecimenType<FactoryMethodWithGenericArgument<Integer>>() {}, noContext());
281+
var result = sut.manufacture(new SpecimenType<FactoryMethodWithGenericArgument<Integer>>() {}, noContext(), null);
254282

255283
assertThat(result.getValue()).isNotNull();
256284
}
@@ -260,7 +288,7 @@ void factoryMethodWithGenericArgument() {
260288
void genericNoArgumentFactoryMethod() {
261289
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
262290

263-
var result = sut.manufacture(new SpecimenType<GenericClassWithFactoryMethodWithoutArgument<Integer>>() {}, noContext());
291+
var result = sut.manufacture(new SpecimenType<GenericClassWithFactoryMethodWithoutArgument<Integer>>() {}, noContext(), null);
264292

265293
assertThat(result).isNotNull();
266294
assertThat(result.getValue()).isEqualTo(42);
@@ -461,7 +489,7 @@ class ProxyTest {
461489
void callsDefaultMethods() {
462490
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
463491

464-
var actual = (InterfaceWithDefaultMethod) sut.proxy(new SpecimenType<InterfaceWithDefaultMethod>() {}, new HashMap<String, ISpecimen<?>>());
492+
var actual = (InterfaceWithDefaultMethod) sut.proxy(new SpecimenType<InterfaceWithDefaultMethod>() {}, new HashMap<>());
465493

466494
assertThat(actual.getTestObject()).isNotNull();
467495
assertThat(actual.getDefaultInt()).isEqualTo(42);
@@ -472,7 +500,7 @@ void callsDefaultMethods() {
472500
void fromAbstractClass() {
473501
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));
474502

475-
var actual = (AbstractClassWithConcreteMethod) sut.proxy(new SpecimenType<AbstractClassWithConcreteMethod>() {}, new HashMap<String, ISpecimen<?>>());
503+
var actual = (AbstractClassWithConcreteMethod) sut.proxy(new SpecimenType<AbstractClassWithConcreteMethod>() {}, new HashMap<>());
476504

477505
assertThat(actual.getTestObject()).isNotNull();
478506
assertThat(actual.getDefaultInt()).isEqualTo(42);

src/test/java/com/github/nylle/javafixture/specimen/AbstractSpecimenTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import com.github.nylle.javafixture.Configuration;
44
import com.github.nylle.javafixture.Context;
5+
import com.github.nylle.javafixture.SpecimenException;
56
import com.github.nylle.javafixture.SpecimenFactory;
67
import com.github.nylle.javafixture.SpecimenType;
78
import com.github.nylle.javafixture.testobjects.TestAbstractClass;
89
import com.github.nylle.javafixture.testobjects.abstractclasses.AbstractClassWithConcreteMethod;
10+
import com.github.nylle.javafixture.testobjects.abstractclasses.AbstractClassWithConstructorException;
911
import com.github.nylle.javafixture.testobjects.interfaces.InterfaceWithoutImplementation;
1012
import org.junit.jupiter.api.BeforeEach;
1113
import org.junit.jupiter.api.DisplayName;
@@ -14,6 +16,7 @@
1416

1517
import java.io.IOException;
1618
import java.lang.annotation.Annotation;
19+
import java.lang.reflect.InvocationTargetException;
1720
import java.nio.charset.Charset;
1821
import java.util.ArrayList;
1922
import java.util.List;
@@ -123,6 +126,24 @@ void creatingAbstractClassWithoutConstructorFallsBackToManufacturing() {
123126
assertThat(context.isCached(specimenType)).isFalse();
124127
}
125128

129+
@Test
130+
void exceptionsArePassedOnToManufacturingFallback() {
131+
var specimenType = SpecimenType.fromClass(AbstractClassWithConstructorException.class);
132+
var sut = new AbstractSpecimen<>(specimenType, context, specimenFactory);
133+
134+
assertThatExceptionOfType(SpecimenException.class)
135+
.isThrownBy(() -> sut.create(noContext(), new Annotation[0]))
136+
.withMessage("Cannot create instance of class com.github.nylle.javafixture.testobjects.abstractclasses.AbstractClassWithConstructorException: no factory-method found")
137+
.havingCause()
138+
.isInstanceOf(SpecimenException.class)
139+
.withMessage("Unable to create instance of abstract class com.github.nylle.javafixture.testobjects.abstractclasses.AbstractClassWithConstructorException: null")
140+
.havingCause()
141+
.isInstanceOf(InvocationTargetException.class)
142+
.havingCause()
143+
.isInstanceOf(IllegalArgumentException.class)
144+
.withMessage("expected for tests");
145+
}
146+
126147
@Test
127148
void resultIsNotCached() {
128149

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.nylle.javafixture.testobjects.abstractclasses;
2+
3+
public abstract class AbstractClassWithConstructorException {
4+
public AbstractClassWithConstructorException() {
5+
throw new IllegalArgumentException("expected for tests");
6+
}
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.nylle.javafixture.testobjects.withconstructor;
2+
3+
public class ConstructorExceptionAndNoFactoryMethod {
4+
public ConstructorExceptionAndNoFactoryMethod() {
5+
throw new IllegalArgumentException("expected for tests");
6+
}
7+
}

0 commit comments

Comments
 (0)