From 64d9570e08b361429c80951aacc09febeb79fc65 Mon Sep 17 00:00:00 2001 From: Mahmoud Ben Hassine Date: Wed, 25 Mar 2020 01:20:47 +0100 Subject: [PATCH] Fail the randomization process if a setter invocation fails Before this commit, if the invocation of a setter fails, Easy Random will set the field via reflection. This means Easy Random might not respect the behaviour that the class designer has intended and can generate invalid random instances. This commit makes Easy Random fail by default in such cases. Resolves #400 --- .../java/org/jeasy/random/FieldPopulator.java | 9 ++++- .../jeasy/random/util/ReflectionUtils.java | 4 +- .../java/org/jeasy/random/EasyRandomTest.java | 17 ++++++++ .../java/org/jeasy/random/beans/Salary.java | 40 +++++++++++++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 easy-random-core/src/test/java/org/jeasy/random/beans/Salary.java diff --git a/easy-random-core/src/main/java/org/jeasy/random/FieldPopulator.java b/easy-random-core/src/main/java/org/jeasy/random/FieldPopulator.java index 8132dc91c..d7d1be167 100644 --- a/easy-random-core/src/main/java/org/jeasy/random/FieldPopulator.java +++ b/easy-random-core/src/main/java/org/jeasy/random/FieldPopulator.java @@ -29,6 +29,7 @@ import org.jeasy.random.randomizers.misc.SkipRandomizer; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import static org.jeasy.random.util.CollectionUtils.randomElementOf; @@ -90,7 +91,13 @@ void populateField(final Object target, final Field field, final RandomizationCo throw new ObjectCreationException(exceptionMessage, e); } } - setProperty(target, field, value); + try { + setProperty(target, field, value); + } catch (InvocationTargetException e) { + String exceptionMessage = String.format("Unable to invoke setter for field %s of class %s", + field.getName(), target.getClass().getName()); + throw new ObjectCreationException(exceptionMessage, e.getCause()); + } } context.popStackItem(); } diff --git a/easy-random-core/src/main/java/org/jeasy/random/util/ReflectionUtils.java b/easy-random-core/src/main/java/org/jeasy/random/util/ReflectionUtils.java index cd8643989..1d9627921 100644 --- a/easy-random-core/src/main/java/org/jeasy/random/util/ReflectionUtils.java +++ b/easy-random-core/src/main/java/org/jeasy/random/util/ReflectionUtils.java @@ -128,7 +128,7 @@ public static List getInheritedFields(Class type) { * @param value value to set * @throws IllegalAccessException if the property cannot be set */ - public static void setProperty(final Object object, final Field field, final Object value) throws IllegalAccessException { + public static void setProperty(final Object object, final Field field, final Object value) throws IllegalAccessException, InvocationTargetException { try { PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), object.getClass()); Method setter = propertyDescriptor.getWriteMethod(); @@ -137,7 +137,7 @@ public static void setProperty(final Object object, final Field field, final Obj } else { setFieldValue(object, field, value); } - } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) { + } catch (IntrospectionException | IllegalAccessException e) { setFieldValue(object, field, value); } } diff --git a/easy-random-core/src/test/java/org/jeasy/random/EasyRandomTest.java b/easy-random-core/src/test/java/org/jeasy/random/EasyRandomTest.java index 005f6ebd6..032b22b3f 100644 --- a/easy-random-core/src/test/java/org/jeasy/random/EasyRandomTest.java +++ b/easy-random-core/src/test/java/org/jeasy/random/EasyRandomTest.java @@ -66,6 +66,23 @@ void generatedBeansShouldBeCorrectlyPopulated() { validatePerson(person); } + @Test + void shouldFailIfSetterInvocationFails() { + EasyRandom easyRandom = new EasyRandom(); + Throwable thrown = catchThrowable(() -> easyRandom.nextObject(Salary.class)); + + assertThat(thrown).isInstanceOf(ObjectCreationException.class) + .hasMessageContaining("Unable to create a random instance of type class org.jeasy.random.beans.Salary"); + + Throwable cause = thrown.getCause(); + assertThat(cause).isInstanceOf(ObjectCreationException.class) + .hasMessageContaining("Unable to invoke setter for field amount of class org.jeasy.random.beans.Salary"); + + Throwable rootCause = cause.getCause(); + assertThat(rootCause).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Amount must be positive"); + } + @Test void finalFieldsShouldBePopulated() { Person person = easyRandom.nextObject(Person.class); diff --git a/easy-random-core/src/test/java/org/jeasy/random/beans/Salary.java b/easy-random-core/src/test/java/org/jeasy/random/beans/Salary.java new file mode 100644 index 000000000..63724cbd0 --- /dev/null +++ b/easy-random-core/src/test/java/org/jeasy/random/beans/Salary.java @@ -0,0 +1,40 @@ +/* + * The MIT License + * + * Copyright (c) 2020, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jeasy.random.beans; + +public class Salary { + + private int amount; + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + if (amount <= 0) { + throw new IllegalArgumentException("Amount must be positive"); + } + this.amount = amount; + } +}