Skip to content

Commit 51bcbe1

Browse files
committed
fix: improve error-message for unknown nested field
When a nested field is customized (e.g. `.with("parent.xxx", 42)`), but the field `xxx` doesn't exist in `parent`, a proper exception is thrown, but then misinterpreted as a failure to construct the object via reflection, then retried using the constructor which then fails ultimately with a misleading message. Instead, we introduced a specialized exception for customization-validation that will bubble up with the proper error-message without being misinterpreted.
1 parent c03e9a7 commit 51bcbe1

File tree

7 files changed

+44
-15
lines changed

7 files changed

+44
-15
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.nylle.javafixture;
2+
3+
public class CustomizationException extends RuntimeException {
4+
public CustomizationException(String message) {
5+
super(message);
6+
}
7+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ private static <T> List<Constructor<T>> findValidConstructors(SpecimenType<T> ty
253253
var result = candidates.stream().filter(x -> x.getValue().isEmpty()).map(x -> x.getKey()).collect(toList());
254254

255255
if (result.isEmpty()) {
256-
throw new SpecimenException(Stream.concat(
256+
throw new CustomizationException(Stream.concat(
257257
Stream.of("Cannot customize fields because no suitable constructor was found. Candidates are:"),
258258
candidates.stream().map(x -> String.format(" %s(%s) (Missing fields: %s)",
259259
x.getKey().getName(),

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public Reflector<T> validateCustomization(CustomizationContext customizationCont
3434
.findFirst();
3535

3636
if (missingDeclaredField.isPresent()) {
37-
throw new SpecimenException(format("Cannot customize field '%s': Field not found in class '%s'.", missingDeclaredField.get(), type.getName()));
37+
throw new CustomizationException(format("Cannot customize field '%s': Field not found in class '%s'.", missingDeclaredField.get(), type.getName()));
3838
}
3939

4040
var duplicateField = getDeclaredFields(type.asClass())
@@ -48,7 +48,7 @@ public Reflector<T> validateCustomization(CustomizationContext customizationCont
4848
.findFirst();
4949

5050
if (duplicateField.isPresent()) {
51-
throw new SpecimenException(format("Cannot customize field '%s'. Duplicate field names found: %n%s",
51+
throw new CustomizationException(format("Cannot customize field '%s'. Duplicate field names found: %n%s",
5252
duplicateField.get().getKey(),
5353
duplicateField.get().getValue().stream().map(x -> x.toString()).collect(joining("\n"))));
5454
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ void throwsExceptionForInvalidCustomization() {
7171
"arg1.nested", new Object(),
7272
"missingCustomization", new Object()), false);
7373

74-
assertThatExceptionOfType(SpecimenException.class)
74+
assertThatExceptionOfType(CustomizationException.class)
7575
.isThrownBy(() -> sut.construct(type, context))
7676
.withMessageContaining(format("Cannot customize fields because no suitable constructor was found. Candidates are:%n"))
7777
.withMessageContaining(format(" com.github.nylle.javafixture.testobjects.withconstructor.TestObjectWithTwoConstructors(arg0,arg1) (Missing fields: [missingCustomization, missingExclusion])%n"))

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ void invalidCustomization() {
5252

5353
var invalidCustomisation = new CustomizationContext(List.of(), Map.of("nonExistingField.subField", "foo"), false);
5454

55-
assertThatExceptionOfType(SpecimenException.class)
55+
assertThatExceptionOfType(CustomizationException.class)
5656
.isThrownBy(() -> sut.validateCustomization(invalidCustomisation))
5757
.withMessage("Cannot customize field 'nonExistingField': Field not found in class 'com.github.nylle.javafixture.testobjects.inheritance.GenericChild<java.lang.String>'.")
5858
.withNoCause();
@@ -68,7 +68,7 @@ void customizingDuplicateFields() {
6868

6969
var invalidCustomisation = new CustomizationContext(List.of(), customization, false);
7070

71-
assertThatExceptionOfType(SpecimenException.class)
71+
assertThatExceptionOfType(CustomizationException.class)
7272
.isThrownBy(() -> sut.validateCustomization(invalidCustomisation))
7373
.withMessageContaining("Cannot customize field 'fieldIn2Classes'. Duplicate field names found:")
7474
.withMessageContaining("private java.lang.Double com.github.nylle.javafixture.testobjects.inheritance.GenericParent.fieldIn2Classes")
@@ -86,7 +86,7 @@ void omittingDuplicateFields() {
8686

8787
var invalidCustomisation = new CustomizationContext(omitting, Map.of(), false);
8888

89-
assertThatExceptionOfType(SpecimenException.class)
89+
assertThatExceptionOfType(CustomizationException.class)
9090
.isThrownBy(() -> sut.validateCustomization(invalidCustomisation))
9191
.withMessageContaining("Cannot customize field 'fieldIn2Classes'. Duplicate field names found:")
9292
.withMessageContaining("private java.lang.Double com.github.nylle.javafixture.testobjects.inheritance.GenericParent.fieldIn2Classes")

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.github.nylle.javafixture.Configuration;
44
import com.github.nylle.javafixture.Context;
55
import com.github.nylle.javafixture.CustomizationContext;
6-
import com.github.nylle.javafixture.SpecimenException;
6+
import com.github.nylle.javafixture.CustomizationException;
77
import com.github.nylle.javafixture.SpecimenFactory;
88
import com.github.nylle.javafixture.SpecimenType;
99
import com.github.nylle.javafixture.annotations.testcases.TestCase;
@@ -265,7 +265,7 @@ void firstFieldPerNameIsCustomized() {
265265
"fieldIn3Classes", "foo",
266266
"fieldIn2Classes", 100.0);
267267

268-
assertThatExceptionOfType(SpecimenException.class)
268+
assertThatExceptionOfType(CustomizationException.class)
269269
.isThrownBy(() -> sut.create(new CustomizationContext(List.of(), customization, false), new Annotation[0]));
270270
}
271271

@@ -278,7 +278,7 @@ void firstFieldPerNameIsOmitted() {
278278
"fieldIn3Classes",
279279
"fieldIn2Classes");
280280

281-
assertThatExceptionOfType(SpecimenException.class)
281+
assertThatExceptionOfType(CustomizationException.class)
282282
.isThrownBy(() -> sut.create(new CustomizationContext(omitting, Map.of(), false), new Annotation[0]));
283283
}
284284
}

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

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.github.nylle.javafixture.Configuration;
44
import com.github.nylle.javafixture.Context;
55
import com.github.nylle.javafixture.CustomizationContext;
6-
import com.github.nylle.javafixture.SpecimenException;
6+
import com.github.nylle.javafixture.CustomizationException;
77
import com.github.nylle.javafixture.SpecimenFactory;
88
import com.github.nylle.javafixture.SpecimenType;
99
import com.github.nylle.javafixture.annotations.testcases.TestCase;
@@ -132,7 +132,18 @@ void cannotSetNonExistingField() {
132132

133133
var customizationContext = new CustomizationContext(List.of(), Map.of("nonExistingField", "foo"), false);
134134

135-
assertThatExceptionOfType(Exception.class)
135+
assertThatExceptionOfType(CustomizationException.class)
136+
.isThrownBy(() -> sut.create(customizationContext, new Annotation[0]))
137+
.withMessage("Cannot customize field 'nonExistingField': Field not found in class 'com.github.nylle.javafixture.testobjects.TestObject'.")
138+
.withNoCause();
139+
}
140+
141+
@Test
142+
void cannotSetNonExistingNestedField() {
143+
var sut = new ObjectSpecimen<WithTestObject>(SpecimenType.fromClass(WithTestObject.class), context, specimenFactory);
144+
var customizationContext = new CustomizationContext(List.of(), Map.of("testObject.nonExistingField", "42"), false);
145+
146+
assertThatExceptionOfType(CustomizationException.class)
136147
.isThrownBy(() -> sut.create(customizationContext, new Annotation[0]))
137148
.withMessage("Cannot customize field 'nonExistingField': Field not found in class 'com.github.nylle.javafixture.testobjects.TestObject'.")
138149
.withNoCause();
@@ -144,7 +155,18 @@ void cannotOmitNonExistingField() {
144155

145156
var customizationContext = new CustomizationContext(List.of("nonExistingField"), Map.of(), false);
146157

147-
assertThatExceptionOfType(Exception.class)
158+
assertThatExceptionOfType(CustomizationException.class)
159+
.isThrownBy(() -> sut.create(customizationContext, new Annotation[0]))
160+
.withMessage("Cannot customize field 'nonExistingField': Field not found in class 'com.github.nylle.javafixture.testobjects.TestObject'.")
161+
.withNoCause();
162+
}
163+
164+
@Test
165+
void cannotOmitNonExistingNestedField() {
166+
var sut = new ObjectSpecimen<WithTestObject>(SpecimenType.fromClass(WithTestObject.class), context, specimenFactory);
167+
var customizationContext = new CustomizationContext(List.of("testObject.nonExistingField"), Map.of(), false);
168+
169+
assertThatExceptionOfType(CustomizationException.class)
148170
.isThrownBy(() -> sut.create(customizationContext, new Annotation[0]))
149171
.withMessage("Cannot customize field 'nonExistingField': Field not found in class 'com.github.nylle.javafixture.testobjects.TestObject'.")
150172
.withNoCause();
@@ -225,7 +247,7 @@ void firstFieldPerNameIsCustomized() {
225247
"fieldIn3Classes", "foo",
226248
"fieldIn2Classes", 100.0);
227249

228-
assertThatExceptionOfType(SpecimenException.class)
250+
assertThatExceptionOfType(CustomizationException.class)
229251
.isThrownBy(() -> sut.create(new CustomizationContext(List.of(), customization, false), new Annotation[0]));
230252
}
231253

@@ -238,7 +260,7 @@ void firstFieldPerNameIsOmitted() {
238260
"fieldIn3Classes",
239261
"fieldIn2Classes");
240262

241-
assertThatExceptionOfType(SpecimenException.class)
263+
assertThatExceptionOfType(CustomizationException.class)
242264
.isThrownBy(() -> sut.create(new CustomizationContext(omitting, Map.of(), false), new Annotation[0]));
243265
}
244266
}

0 commit comments

Comments
 (0)