Last active
October 24, 2025 15:00
-
-
Save MichalBrylka/a63b150697831e677b710ecf895f3631 to your computer and use it in GitHub Desktop.
Random item
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.lang.reflect.*; | |
| import java.util.*; | |
| public class DefaultObjectGenerator { | |
| public <T> T create(Class<T> clazz) { | |
| try { | |
| // Base cases: primitive, string, enum | |
| if (isSimpleType(clazz)) return getPrimitive(clazz); | |
| if (clazz.isEnum()) return clazz.cast(createEnumValue(clazz)); | |
| T instance = clazz.getDeclaredConstructor().newInstance(); | |
| fillFields(instance); | |
| return instance; | |
| } catch (Exception e) { | |
| throw new RuntimeException("Failed to fill " + clazz.getSimpleName(), e); | |
| } | |
| } | |
| private void fillFields(Object obj) { | |
| try { | |
| for (Method method : Arrays.stream(obj.getClass().getMethods()) | |
| .filter(m -> m.getName().startsWith("set") && m.getParameterCount() == 1) | |
| .sorted(Comparator.comparing(Method::getName)) | |
| .toList() | |
| ) { | |
| Class<?> paramType = method.getParameterTypes()[0]; | |
| Object value = createValueForType(paramType); | |
| if (value != null) | |
| method.invoke(obj, value); | |
| } | |
| // handle nested batches again (recursive) | |
| for (Method method : Arrays.stream(obj.getClass().getMethods()) | |
| .filter(m -> m.getName().startsWith("get") && m.getParameterCount() == 0) | |
| .sorted(Comparator.comparing(Method::getName)) | |
| .toList() | |
| ) { | |
| Object val = method.invoke(obj); | |
| if (val != null && isBatchType(val.getClass())) { | |
| fillBatch(val, method); | |
| } | |
| } | |
| } catch (Exception e) { | |
| throw new RuntimeException("Failed to populate " + obj.getClass().getSimpleName(), e); | |
| } | |
| } | |
| private void fillBatch(Object batchObj, Method getter) throws Exception { | |
| // Extract the generic parameter type <T> of Batch<T> | |
| Type returnType = getter.getGenericReturnType(); | |
| if (!(returnType instanceof ParameterizedType pt)) return; | |
| Type[] typeArgs = pt.getActualTypeArguments(); | |
| if (typeArgs.length != 1 || !(typeArgs[0] instanceof Class<?> itemType)) return; | |
| int count = 1; | |
| Method addMethod = batchObj.getClass().getMethod("add"); | |
| for (int i = 0; i < count; i++) { | |
| Object item = addMethod.invoke(batchObj); // batch internally creates new T | |
| if (item != null) { | |
| fillFields(item); | |
| } | |
| } | |
| } | |
| // Utility methods | |
| private static boolean isBatchType(Class<?> clazz) { | |
| return clazz.getSimpleName().equals("Batch"); | |
| } | |
| private static boolean isSimpleType(Class<?> clazz) { | |
| return clazz == String.class || | |
| clazz == byte.class || clazz == Byte.class || | |
| clazz == short.class || clazz == Short.class || | |
| clazz == int.class || clazz == Integer.class || | |
| clazz == long.class || clazz == Long.class || | |
| clazz == double.class || clazz == Double.class || | |
| clazz == float.class || clazz == Float.class || | |
| clazz == char.class || clazz == Character.class || | |
| clazz == boolean.class || clazz == Boolean.class; | |
| } | |
| @SuppressWarnings("unchecked") | |
| private <T> T getPrimitive(Class<T> clazz) { | |
| if (clazz == String.class) return null; | |
| if (clazz == byte.class || clazz == Byte.class) return (T) Byte.valueOf((byte) 0); | |
| if (clazz == short.class || clazz == Short.class) return (T) Short.valueOf((short) 0); | |
| if (clazz == int.class || clazz == Integer.class) return (T) Integer.valueOf(0); | |
| if (clazz == long.class || clazz == Long.class) return (T) Long.valueOf(0L); | |
| if (clazz == double.class || clazz == Double.class) return (T) Double.valueOf(0.0); | |
| if (clazz == float.class || clazz == Float.class) return (T) Float.valueOf(0.0f); | |
| if (clazz == boolean.class || clazz == Boolean.class) return (T) Boolean.FALSE; | |
| if (clazz == char.class || clazz == Character.class) return (T) Character.valueOf('\0'); | |
| return null; | |
| } | |
| private Object createValueForType(Class<?> type) { | |
| if (isSimpleType(type)) return getPrimitive(type); | |
| if (type.isEnum()) return createEnumValue(type); | |
| return create(type); | |
| } | |
| private <E> E createEnumValue(Class<E> enumClass) { | |
| return null; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.lang.reflect.*; | |
| import java.util.*; | |
| public class RandomObjectGenerator { | |
| private final Random random; | |
| public RandomObjectGenerator(long seed) { | |
| this.random = new Random(seed); | |
| } | |
| public <T> T randomize(Class<T> clazz) { | |
| try { | |
| // Base cases: primitive, string, enum | |
| if (isSimpleType(clazz)) return randomPrimitive(clazz); | |
| if (clazz.isEnum()) return clazz.cast(randomEnumValue(clazz)); | |
| T instance = clazz.getDeclaredConstructor().newInstance(); | |
| randomizeFields(instance); | |
| return instance; | |
| } catch (Exception e) { | |
| throw new RuntimeException("Failed to randomize " + clazz.getSimpleName(), e); | |
| } | |
| } | |
| private void randomizeFields(Object obj) { | |
| try { | |
| for (Method method : Arrays.stream(obj.getClass().getMethods()) | |
| .filter(m -> m.getName().startsWith("set") && m.getParameterCount() == 1) | |
| .sorted(Comparator.comparing(Method::getName)) | |
| .toList() | |
| ) { | |
| Class<?> paramType = method.getParameterTypes()[0]; | |
| Object randomValue = randomValueForType(paramType); | |
| if (randomValue != null) { | |
| method.invoke(obj, randomValue); | |
| } | |
| } | |
| // handle nested batches again (recursive) | |
| for (Method method : Arrays.stream(obj.getClass().getMethods()) | |
| .filter(m -> m.getName().startsWith("get") && m.getParameterCount() == 0) | |
| .sorted(Comparator.comparing(Method::getName)) | |
| .toList() | |
| ) { | |
| Object val = method.invoke(obj); | |
| if (val != null && isBatchType(val.getClass())) { | |
| fillBatch(val, method); | |
| } | |
| } | |
| } catch (Exception e) { | |
| throw new RuntimeException("Failed to populate " + obj.getClass().getSimpleName(), e); | |
| } | |
| } | |
| private void fillBatch(Object batchObj, Method getter) throws Exception { | |
| // Extract the generic parameter type <T> of Batch<T> | |
| Type returnType = getter.getGenericReturnType(); | |
| if (!(returnType instanceof ParameterizedType pt)) return; | |
| Type[] typeArgs = pt.getActualTypeArguments(); | |
| if (typeArgs.length != 1 || !(typeArgs[0] instanceof Class<?> itemType)) return; | |
| int count = random.nextInt(1, 4); | |
| Method addMethod = batchObj.getClass().getMethod("add"); | |
| for (int i = 0; i < count; i++) { | |
| Object item = addMethod.invoke(batchObj); // batch internally creates new T | |
| if (item != null) { | |
| randomizeFields(item); | |
| } | |
| } | |
| } | |
| // Utility methods | |
| private static boolean isBatchType(Class<?> clazz) { | |
| return clazz.getSimpleName().equals("Batch"); | |
| } | |
| private static boolean isSimpleType(Class<?> clazz) { | |
| return clazz == String.class || | |
| clazz == byte.class || clazz == Byte.class || | |
| clazz == short.class || clazz == Short.class || | |
| clazz == int.class || clazz == Integer.class || | |
| clazz == long.class || clazz == Long.class || | |
| clazz == double.class || clazz == Double.class || | |
| clazz == float.class || clazz == Float.class || | |
| clazz == char.class || clazz == Character.class || | |
| clazz == boolean.class || clazz == Boolean.class; | |
| } | |
| @SuppressWarnings("unchecked") | |
| private <T> T randomPrimitive(Class<T> clazz) { | |
| if (clazz == String.class) return (T) ("Random" + random.nextInt(1000)); | |
| if (clazz == byte.class || clazz == Byte.class) return (T) Byte.valueOf((byte) random.nextInt(100)); | |
| if (clazz == short.class || clazz == Short.class) return (T) Short.valueOf((short) random.nextInt(100)); | |
| if (clazz == int.class || clazz == Integer.class) return (T) Integer.valueOf(random.nextInt(100)); | |
| if (clazz == long.class || clazz == Long.class) return (T) Long.valueOf(random.nextLong(100)); | |
| if (clazz == double.class || clazz == Double.class) return (T) Double.valueOf(random.nextDouble()); | |
| if (clazz == float.class || clazz == Float.class) return (T) Float.valueOf(random.nextFloat()); | |
| if (clazz == char.class || clazz == Character.class) | |
| return (T) Character.valueOf((char) (32 + random.nextInt(95))); | |
| if (clazz == boolean.class || clazz == Boolean.class) return (T) Boolean.valueOf(random.nextBoolean()); | |
| return null; | |
| } | |
| private Object randomValueForType(Class<?> type) { | |
| if (isSimpleType(type)) return randomPrimitive(type); | |
| if (type.isEnum()) return randomEnumValue(type); | |
| return randomize(type); | |
| } | |
| private <E> E randomEnumValue(Class<E> enumClass) { | |
| E[] values = enumClass.getEnumConstants(); | |
| return values[random.nextInt(values.length)]; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.lang.reflect.ParameterizedType; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import java.util.stream.Stream; | |
| import org.junit.jupiter.api.*; | |
| import org.junit.jupiter.params.ParameterizedTest; | |
| import org.junit.jupiter.params.provider.Arguments; | |
| import org.junit.jupiter.params.provider.MethodSource; | |
| import static org.assertj.core.api.Assertions.*; | |
| class RandomObjectGeneratorTest { | |
| // 🔹 Provide both class and expected deterministic value for primitives | |
| static Stream<Arguments> simpleClasses() { | |
| return Stream.of( | |
| Arguments.of(String.class, "Random130"), | |
| Arguments.of(Integer.class, 30), | |
| Arguments.of(Boolean.class, true), | |
| Arguments.of(Double.class, 0.727563) | |
| ); | |
| } | |
| // 🔹 Provide expected attributes for complex classes | |
| static Stream<Arguments> complexClasses() { | |
| return Stream.of( | |
| Arguments.of(Address.class, new Address("Random130", 63)), | |
| Arguments.of(Job.class, new Job("Random763", 30)), | |
| Arguments.of(Person.class, new Person("Random970", 84, true, Role.USER, new Address("Random763", 48), GetSampleBatch())) | |
| ); | |
| } | |
| private static Batch<Job> GetSampleBatch() { | |
| Batch<Job> jobBatch = new Batch<>(Job.class); | |
| // Add 3 jobs | |
| Job job1 = jobBatch.add(); | |
| job1.setTitle("Random519"); | |
| job1.setSalary(18); | |
| Job job2 = jobBatch.add(); | |
| job2.setTitle("Random182"); | |
| job2.setSalary(93); | |
| Job job3 = jobBatch.add(); | |
| job3.setTitle("Random276"); | |
| job3.setSalary(2); | |
| return jobBatch; | |
| } | |
| @ParameterizedTest | |
| @MethodSource("simpleClasses") | |
| @DisplayName("should generate deterministic primitive-like values given seed=42") | |
| void testSimpleRandomGenerationDeterministic(Class<?> clazz, Object expected) { | |
| Object result = new RandomObjectGenerator(42).randomize(clazz); | |
| assertThat(result) | |
| .as("random instance for %s", clazz.getSimpleName()) | |
| .isNotNull(); | |
| if (expected instanceof Double d) | |
| assertThat((Double) result).isCloseTo(d, within(1e-6)); | |
| else if (expected instanceof Float f) | |
| assertThat((Double) result).isCloseTo(f, within(1e-6)); | |
| else | |
| assertThat(result).isEqualTo(expected); | |
| } | |
| @ParameterizedTest | |
| @MethodSource("complexClasses") | |
| @DisplayName("should generate deterministic complex objects given seed=42") | |
| void testComplexRandomGenerationDeterministic(Class<?> clazz, Object expected) { | |
| Object result = new RandomObjectGenerator(42).randomize(clazz); | |
| assertThat(result).usingRecursiveComparison().isEqualTo(expected); | |
| } | |
| @Test | |
| @DisplayName("should fill Batch<Job> recursively and deterministically") | |
| void testBatchFilling() { | |
| Person person = new RandomObjectGenerator(42).randomize(Person.class); | |
| assertThat(person).isNotNull(); | |
| assertThat(person.getJobs()).isNotNull(); | |
| List<Job> jobs = person.getJobs().getItems(); | |
| assertThat(jobs) | |
| .isNotNull() | |
| .isNotEmpty() | |
| .allSatisfy(job -> { | |
| assertThat(job.getTitle()).startsWith("Random"); | |
| assertThat(job.getSalary()).isBetween(0, 99); | |
| }); | |
| // deterministic for seed 42 — number of jobs created between 1–3 | |
| assertThat(jobs.size()).isEqualTo(3); | |
| } | |
| } | |
| enum Role { | |
| ADMIN, USER, GUEST | |
| } | |
| @lombok.AllArgsConstructor | |
| @lombok.NoArgsConstructor | |
| @lombok.Getter | |
| @lombok.Setter | |
| class Address { | |
| private String city; | |
| private int zip; | |
| } | |
| @lombok.NoArgsConstructor | |
| @lombok.AllArgsConstructor | |
| @lombok.Getter | |
| @lombok.Setter | |
| class Job { | |
| private String title; | |
| private int salary; | |
| } | |
| @lombok.Getter | |
| @lombok.AllArgsConstructor | |
| @lombok.NoArgsConstructor | |
| class Person { | |
| @lombok.Setter String name; | |
| @lombok.Setter int age; | |
| @lombok.Setter boolean active; | |
| @lombok.Setter Role role; | |
| @lombok.Setter Address address = null; | |
| Batch<Job> jobs = new Batch<>(Job.class); | |
| } | |
| /** | |
| * Updated Batch<T> that internally creates new items on add() | |
| */ | |
| class Batch<T> { | |
| private final List<T> list = new ArrayList<>(); | |
| private final Class<T> type; | |
| public Batch(Class<T> type) { | |
| this.type = type; | |
| } | |
| @SuppressWarnings("unchecked") | |
| public T add() { | |
| try { | |
| try { | |
| T item = type.getDeclaredConstructor().newInstance(); | |
| list.add(item); | |
| return item; | |
| } catch (Exception e) { | |
| throw new RuntimeException("Unable to create new Batch item", e); | |
| } | |
| } catch (Exception e) { | |
| throw new RuntimeException("Unable to add new Batch item", e); | |
| } | |
| } | |
| public List<T> getItems() { | |
| return list; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment