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`. 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/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/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..72b523c 100644 --- a/src/test/java/com/github/nylle/javafixture/FixtureTest.java +++ b/src/test/java/com/github/nylle/javafixture/FixtureTest.java @@ -217,6 +217,18 @@ void canOmitPrivateField() { assertThat(result.getHello()).isNull(); } + @Test + void canOmitFieldsOfClassByType() { + var fixture = new Fixture(configuration); + + 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 void canCustomizeOptional() { Fixture fixture = new Fixture(configuration); 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)