-
Notifications
You must be signed in to change notification settings - Fork 236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to generate random values to record type #514
Comments
Hi! This is not a bug, but a feature that's being implemented: This project is in maintaince mode, so sadly I think there is no estimation of when the feature will be finally released. |
We're using a workaround to generate random data for Java records. The process works by dynamically creating a companion class at runtime, passing it to the randomizer, and then copying the generated values to the record. import net.bytebuddy.ByteBuddy;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.description.modifier.Visibility;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;
public class DefaultEasyRandom extends EasyRandom {
private static final NamingStrategy.Suffixing namingStrategy = new NamingStrategy.Suffixing("RecordClassAdapter");
private static final ConcurrentMap<Class<?>, Class<?>> dynamicClassCache = new ConcurrentHashMap<>();
private final EasyRandomParameters parameters;
public DefaultEasyRandom(EasyRandomParameters parameters) {
super(parameters);
this.parameters = parameters;
}
@Override
public <T> T nextObject(Class<T> type) {
return doPopulateBean(type, new RandomizationContext(type, parameters));
}
@Override
public <T> Stream<T> objects(Class<T> type, int streamSize) {
return super.objects(type, streamSize);
}
@Override
<T> T doPopulateBean(Class<T> type, RandomizationContext context) {
return type.isRecord() ? this.doPopulateRecord(type, context) : super.doPopulateBean(type, context);
}
<T> T doPopulateRecord(Class<T> type, RandomizationContext context) {
try {
return doPopulateRecordInternal(type, context);
} catch (Exception e) {
return sneakyThrow(e);
}
}
private <T> T doPopulateRecordInternal(Class<T> type, RandomizationContext context) throws Exception {
Class<T> targetType = getDynamicRecordClassAdapter(type);
final var result = super.doPopulateBean(targetType, context);
final var sourceFields = getFields(targetType);
final var targetParams = new Object[sourceFields.size()];
for (int i = 0; i < sourceFields.size(); i++) {
final var field = sourceFields.get(i);
targetParams[i] = field.get(result);
}
return newInstance(type, targetParams);
}
@SuppressWarnings("unchecked")
private <T> Class<T> getDynamicRecordClassAdapter(Class<T> targetType) {
return (Class<T>) dynamicClassCache.computeIfAbsent(targetType, this::defineDynamicRecordClassAdapter);
}
private Class<?> defineDynamicRecordClassAdapter(Class<?> targetType) {
if (!targetType.isRecord()) {
throw new IllegalArgumentException("Only records are supported");
}
final var fields = getFields(targetType);
var definitionBuilder = new ByteBuddy().with(namingStrategy).subclass(Object.class);
for (Field field : fields) {
definitionBuilder = definitionBuilder.defineField(field.getName(), field.getType(), Visibility.PRIVATE);
}
try (final var dynamicType = definitionBuilder.make()) {
final var loadedType = dynamicType.load(targetType.getClassLoader());
return loadedType.getLoaded();
} catch (Exception e) {
return sneakyThrow(e);
}
}
private static <A> List<Field> getFields(Class<A> targetType) {
final var declaredFields = targetType.getDeclaredFields();
final var result = new ArrayList<Field>(declaredFields.length);
for (var field : declaredFields) {
if (!Modifier.isStatic(field.getModifiers())) {
field.setAccessible(true);
result.add(field);
}
}
return result;
}
@SuppressWarnings("unchecked")
private static <A> A newInstance(Class<A> type, Object... params) {
try {
if (type.getConstructors().length == 0) {
throw new IllegalArgumentException("No constructor found in class " + type);
}
return (A) type.getConstructors()[0].newInstance(params);
} catch (Exception e) {
return sneakyThrow(e);
}
}
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
private static <T, E extends Throwable> T sneakyThrow(Throwable e) throws E {
throw (E) e;
}
} |
I'm trying to use
records
with easy-random 5.0.0, but I was unable to run properly, as you can see in the following code.And the runner is
And I got this error:
My Java version
Thank U
The text was updated successfully, but these errors were encountered: