From ec4392267b4aba7da045d1ba1aea0881c399263a Mon Sep 17 00:00:00 2001 From: Andreas Kutschera Date: Tue, 20 Jan 2026 17:49:23 +0100 Subject: [PATCH 1/3] feat: omit fields by class type #121 Now you can do "without(String.class)" and you will not have any field set that is of this type. --- src/main/java/com/github/nylle/javafixture/Context.java | 3 ++- .../com/github/nylle/javafixture/ISpecimenBuilder.java | 2 ++ .../com/github/nylle/javafixture/SpecimenBuilder.java | 9 +++++++-- .../java/com/github/nylle/javafixture/FixtureTest.java | 9 +++++++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/nylle/javafixture/Context.java b/src/main/java/com/github/nylle/javafixture/Context.java index 1d182a0..a37b7f7 100644 --- a/src/main/java/com/github/nylle/javafixture/Context.java +++ b/src/main/java/com/github/nylle/javafixture/Context.java @@ -1,5 +1,6 @@ package com.github.nylle.javafixture; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -24,7 +25,7 @@ public Context(Configuration configuration, Map, Object> predefi } this.configuration = configuration; - this.cache = new ConcurrentHashMap<>(predefinedInstances); + this.cache = new HashMap<>(predefinedInstances); } public Configuration getConfiguration() { diff --git a/src/main/java/com/github/nylle/javafixture/ISpecimenBuilder.java b/src/main/java/com/github/nylle/javafixture/ISpecimenBuilder.java index f4b1d98..8ed57e3 100644 --- a/src/main/java/com/github/nylle/javafixture/ISpecimenBuilder.java +++ b/src/main/java/com/github/nylle/javafixture/ISpecimenBuilder.java @@ -22,5 +22,7 @@ public interface ISpecimenBuilder { ISpecimenBuilder with(SpecimenType type, U value); ISpecimenBuilder without(String fieldName); + + ISpecimenBuilder without(Class fieldName); } diff --git a/src/main/java/com/github/nylle/javafixture/SpecimenBuilder.java b/src/main/java/com/github/nylle/javafixture/SpecimenBuilder.java index 9aebd27..e1b035b 100644 --- a/src/main/java/com/github/nylle/javafixture/SpecimenBuilder.java +++ b/src/main/java/com/github/nylle/javafixture/SpecimenBuilder.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -15,7 +14,7 @@ public class SpecimenBuilder implements ISpecimenBuilder { private final List> functions = new LinkedList<>(); private final List ignoredFields = new LinkedList<>(); private final Map customFields = new HashMap<>(); - private final Map, Object> predefinedInstances = new ConcurrentHashMap<>(); + private final Map, Object> predefinedInstances = new HashMap<>(); private final SpecimenType type; private final Configuration configuration; @@ -129,6 +128,12 @@ public ISpecimenBuilder without(final String fieldName) { return this; } + @Override + public ISpecimenBuilder without(Class fieldName) { + predefinedInstances.put(SpecimenType.fromClass(fieldName), null); + return this; + } + T construct() { return new SpecimenFactory(new Context(configuration)).build(type).create(new CustomizationContext(List.of(), Map.of(), true), new Annotation[0]); } diff --git a/src/test/java/com/github/nylle/javafixture/FixtureTest.java b/src/test/java/com/github/nylle/javafixture/FixtureTest.java index 1f78401..d8b31d9 100644 --- a/src/test/java/com/github/nylle/javafixture/FixtureTest.java +++ b/src/test/java/com/github/nylle/javafixture/FixtureTest.java @@ -217,6 +217,15 @@ void canOmitPrivateField() { assertThat(result.getHello()).isNull(); } + @Test + void canOmitFieldsOfClassByType() { + var fixture = new Fixture(configuration); + + TestPrimitive result = fixture.build(TestPrimitive.class).without(String.class).create(); + + assertThat(result.getHello()).isNull(); + } + @Test void canCustomizeOptional() { Fixture fixture = new Fixture(configuration); From fd8b4c18af4165b69a20a382d057a1f4cefad3fa Mon Sep 17 00:00:00 2001 From: Andreas Kutschera Date: Sat, 24 Jan 2026 14:25:55 +0100 Subject: [PATCH 2/3] feat: omit primitive fields by class type #121 Now you can do "without(int.class)" and primitive fields will have their default value. --- .../github/nylle/javafixture/Reflector.java | 6 ++- .../github/nylle/javafixture/FixtureTest.java | 5 +- .../nylle/javafixture/ReflectorTest.java | 48 +++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/nylle/javafixture/Reflector.java b/src/main/java/com/github/nylle/javafixture/Reflector.java index 819750d..f25d73d 100644 --- a/src/main/java/com/github/nylle/javafixture/Reflector.java +++ b/src/main/java/com/github/nylle/javafixture/Reflector.java @@ -75,8 +75,10 @@ public Annotation[] getFieldAnnotations(Field field) { public void setField(Field field, Object value) { try { - field.setAccessible(true); - field.set(instance, value); + if (!field.getType().isPrimitive() || value != null) { + field.setAccessible(true); + field.set(instance, value); + } } catch (SecurityException e) { throw new SpecimenException(format("Unable to access field %s on object of type %s", field.getName(), type.getName()), e); } catch (IllegalAccessException | InaccessibleObjectException e) { diff --git a/src/test/java/com/github/nylle/javafixture/FixtureTest.java b/src/test/java/com/github/nylle/javafixture/FixtureTest.java index d8b31d9..72b523c 100644 --- a/src/test/java/com/github/nylle/javafixture/FixtureTest.java +++ b/src/test/java/com/github/nylle/javafixture/FixtureTest.java @@ -221,9 +221,12 @@ void canOmitPrivateField() { void canOmitFieldsOfClassByType() { var fixture = new Fixture(configuration); - TestPrimitive result = fixture.build(TestPrimitive.class).without(String.class).create(); + TestPrimitive result = fixture.build(TestPrimitive.class).without(String.class) + .without(int.class).create(); assertThat(result.getHello()).isNull(); + assertThat(result.getInteger()).isNotNull(); + assertThat(result.getPrimitive()).isZero(); } @Test diff --git a/src/test/java/com/github/nylle/javafixture/ReflectorTest.java b/src/test/java/com/github/nylle/javafixture/ReflectorTest.java index a4668db..9d19f30 100644 --- a/src/test/java/com/github/nylle/javafixture/ReflectorTest.java +++ b/src/test/java/com/github/nylle/javafixture/ReflectorTest.java @@ -22,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -99,10 +100,55 @@ void omittingDuplicateFields() { @DisplayName("when setting a field via reflection") class SetField { + private String stringField; + private int primitiveField; + + @DisplayName("an Object field can be set") + @Test + void setObject() throws Exception { + var field = this.getClass().getDeclaredField("stringField"); + var sut = new Reflector<>(this, new SpecimenType<>(){}); + sut.setField(field, "new value"); + + assertThat(stringField).isEqualTo("new value"); + } + + @DisplayName("an Object field can be set to null") + @Test + void setObjectToNull() throws Exception { + stringField = "before"; + var field = this.getClass().getDeclaredField("stringField"); + var sut = new Reflector<>(this, new SpecimenType<>(){}); + sut.setField(field, null); + + assertThat(stringField).isNull(); + } + + @DisplayName("a primitive field can be set") + @Test + void setPrimitiveField() throws Exception { + var field = this.getClass().getDeclaredField("primitiveField"); + var sut = new Reflector<>(this, new SpecimenType<>(){}); + sut.setField(field, 4); + + assertThat(primitiveField).isEqualTo(4); + } + + @DisplayName("setting a primitive field to null keeps the default value") + @Test + void setPrimitiveFieldToNull() throws Exception { + var field = this.getClass().getDeclaredField("primitiveField"); + var sut = new Reflector<>(this, new SpecimenType<>(){}); + sut.setField(field, null); + + assertThat(primitiveField).isEqualTo(0); + } + @DisplayName("an IllegalAccessException is turned into a SpecimenException") @Test void catchIllegalAccessException() throws Exception { var mockedField = Mockito.mock(Field.class); + doReturn(this.getClass()).when(mockedField).getType(); var sut = new Reflector<>("", new SpecimenType<>(){}); doThrow(new IllegalAccessException("expected")).when(mockedField).set(any(), any()); @@ -114,6 +160,7 @@ void catchIllegalAccessException() throws Exception { @Test void catchSecurityException() { var mockedField = Mockito.mock(Field.class); + doReturn(this.getClass()).when(mockedField).getType(); var sut = new Reflector<>("", new SpecimenType<>(){}); doThrow(new SecurityException("expected")).when(mockedField).setAccessible(true); assertThatExceptionOfType(SpecimenException.class) @@ -124,6 +171,7 @@ void catchSecurityException() { @Test void catchInaccessibleObjectException() { var mockedField = Mockito.mock(Field.class); + doReturn(this.getClass()).when(mockedField).getType(); var sut = new Reflector<>("", new SpecimenType<>(){}); doThrow(new InaccessibleObjectException("expected")).when(mockedField).setAccessible(true); assertThatExceptionOfType(SpecimenException.class) From 81dae9ca35cc480c97a6f7c07ede22ebfdeef907 Mon Sep 17 00:00:00 2001 From: Andreas Kutschera Date: Sat, 24 Jan 2026 14:33:55 +0100 Subject: [PATCH 3/3] docs: omit fields by class type #121 --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 1187d7c..992e4a7 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,18 @@ TestDto: - myPrivateField: String: null - myPublicField: int: 0 +### Omit all fields for type +```java +var result = fixture.build(TestDto.class) + .without(String.class) + .without(int.class) + .create(); +``` +#### Sample Result +TestDto: +- myPrivateField: String: null +- myPublicField: int: 0 + #### Note Primitives will receive their default-value, classes will be `null`.