Skip to content

Commit

Permalink
Add support to generate random records
Browse files Browse the repository at this point in the history
Resolves #397
  • Loading branch information
fmbenhassine committed Jun 30, 2023
1 parent b24b060 commit 6e67abf
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 1 deletion.
22 changes: 21 additions & 1 deletion easy-random-core/src/main/java/org/jeasy/random/EasyRandom.java
Expand Up @@ -28,6 +28,7 @@
import org.jeasy.random.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.RecordComponent;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
Expand Down Expand Up @@ -97,7 +98,11 @@ public EasyRandom(final EasyRandomParameters easyRandomParameters) {
* @throws ObjectCreationException when unable to create a new instance of the given type
*/
public <T> T nextObject(final Class<T> type) {
return doPopulateBean(type, new RandomizationContext(type, parameters));
if (type.isRecord()) {
return createRandomRecord(type);
} else {
return doPopulateBean(type, new RandomizationContext(type, parameters));
}
}

/**
Expand All @@ -117,6 +122,21 @@ public <T> Stream<T> objects(final Class<T> type, final int streamSize) {
return Stream.generate(() -> nextObject(type)).limit(streamSize);
}

private <T> T createRandomRecord(Class<T> recordType) {
// generate random values for record components
RecordComponent[] recordComponents = recordType.getRecordComponents();
Object[] randomValues = new Object[recordComponents.length];
for (int i = 0; i < recordComponents.length; i++) {
randomValues[i] = this.nextObject(recordComponents[i].getType());
}
// create a random instance with random values
try {
return getCanonicalConstructor(recordType).newInstance(randomValues);
} catch (Exception e) {
throw new ObjectCreationException("Unable to create a random instance of recordType " + recordType, e);
}
}

<T> T doPopulateBean(final Class<T> type, final RandomizationContext context) {
if (exclusionPolicy.shouldBeExcluded(type, context)) {
return null;
Expand Down
Expand Up @@ -559,6 +559,30 @@ public static Optional<Method> getReadMethod(Field field) {
return getPublicMethod("is" + capitalizedFieldName, fieldClass);
}

/**
* Get the canonical constructor of a record
* @param recordType the type of the record
* @return the canonical constructor of the record
* @param <T> the generic type of the record
*/
public static <T> Constructor<T> getCanonicalConstructor(Class<T> recordType) {
RecordComponent[] recordComponents = recordType.getRecordComponents();
Class<?>[] componentTypes = new Class<?>[recordComponents.length];
for (int i = 0; i < recordComponents.length; i++) {
// recordComponents are ordered, see javadoc:
// "The components are returned in the same order that they are declared in the record header"
componentTypes[i] = recordComponents[i].getType();
}
try {
return recordType.getDeclaredConstructor(componentTypes);
} catch (NoSuchMethodException e) {
// should not happen, from Record javadoc:
// "A record class has the following mandated members: a public canonical constructor ,
// whose descriptor is the same as the record descriptor;"
throw new RuntimeException("Invalid record definition", e);
}
}

private static String capitalize(String propertyName) {
return propertyName.substring(0, 1).toUpperCase(ENGLISH) + propertyName.substring(1);
}
Expand Down
@@ -0,0 +1,23 @@
package org.jeasy.random;

import org.assertj.core.api.Assertions;
import org.jeasy.random.records.Person;
import org.junit.jupiter.api.Test;

public class RecordCreationTest {

@Test
void testRandomRecordCreation() {
// given
EasyRandom easyRandom = new EasyRandom();

// when
Person person = easyRandom.nextObject(Person.class);

// then
Assertions.assertThat(person).isNotNull();
Assertions.assertThat(person.id()).isNotNull();
Assertions.assertThat(person.name()).isNotNull();
}

}
@@ -0,0 +1,3 @@
package org.jeasy.random.records;

public record Person(Long id, String name){}

0 comments on commit 6e67abf

Please sign in to comment.