dummymaker

DummyMaker :hotsprings:

GitHub Action Quality Gate Status Coverage Maintainability Rating Lines of Code

Library can generate Data Objects filled random data for your tests, database setup, Big Data setups or other workloads. Library is very flexible at tuning.

Library can even do simple export in CSV/JSON/XML/SQL formats.

Documentation for versions 1.X.X in this document.

Dependency :rocket:

Gradle

implementation "com.github.goodforgod:dummymaker:3.3.1"

Maven

<dependency>
    <groupId>com.github.goodforgod</groupId>
    <artifactId>dummymaker</artifactId>
    <version>3.3.1</version>
</dependency>

Content

Factory example

Example how to produce 1001 Users in no time.

All user data is automatically generated and proper generators are selected due to @GenAuto annotation.

Class that is instantiated by factory via build method must have zero argument constructor (Can be private).

@GenAuto
public class User {
 
    /**
     * All fields will have unique data after being processed by factory
     */
    private int number;
    private String name;
    private String surname;
    private List<User> relatives;
}

public static List<User> getUsers() {
    final GenFactory factory = new GenFactory();

    User user = factory.build(User.class);

    // All users have unique data in their fields
    List<User> users = factory.build(User.class, 1000);
    
    return users;
}

Factory Methods

Factory not only instantiate classes but also provides other contracts to manipulate with objects like using supplier.

final GenFactory factory = new GenFactory();

Set<User> users = factory.stream(() -> new User(), 1)
                            .collect(Collectors.toSet());

User user = new User();
User filled = factory.fill(user);

There are more other contracts available just check GenFactory.

Also, factory can export huge amount of data in one attempt when data cannot be proceeded inmemory via export contract.

In such case export that is provided should append file with each export execution. Default IWriter doesn’t do that by default, so such option should be activated.

final GenFactory factory = new GenFactory();
final JsonExporter exporter = new JsonExporter(fileName -> new FileWriter(fileName, false)); // do not delete file before write

factory.export(User.class, 100_000_000_000L, exporter);

Gen Auto Annotation

@GenAuto annotation provides depth field that is required for embedded fields such as relatives in this example.

For such class, each User will have field relatives filled with data up to level 3 in depth. Because each User will have other users as its data and such data can be represented as a tree. So leaf that has its depth of 3 from root will be the last who have field relatives filled with data.

Maximum depth allowed is 20 (Check @GenAuto.MAX parameter).

@GenAuto(depth = 3)
public class User {

    private int number;
    private String name;
    private String surname;
    private List<User> relatives;
}

Factory configuration

In case you have no intention or opportunity to annotate class even with @GenAuto annotation, you can configure all generators and fields you want to fill or ignore with factory GenRules configuration.

Manual Rule

In case you want to fill only specific field to be filled with data you can configure such behavior via GenRule for specific class.

GenRule rule = GenRule.of(User.class)
                .add(NameGenerator.class, "name")
                .add(ShortGenerator.class, int.class);

GenFactory factory = new GenFactory(rule);
User user = factory.build(User.class);

In this case only name and number fields will be filled with data, as they are the only one specified per GenRule configuration.

Auto Rule

In case you want factory automatically fill fields based on their types and names, you can setup auto (same as GenAuto annotation).

Second argument specifies depth of complex objects generation, check this section for more info.

GenRule rule = GenRule.auto(User.class, 2)
                .add(NameGenerator.class, "name")
                .add(ShortGenerator.class, int.class);

GenFactory factory = new GenFactory(rule);
User user = factory.build(User.class);

In this case fields name and number will be filled as previously but also all other fields will be automatically filled.

GenRule rule = GenRule.auto(User.class, 2);

GenFactory factory = new GenFactory(rule);
User user = factory.build(User.class);

This will have same affect as previous , due to fields name and number been automatically filled.

Global Rule

In case you have a lot of common fields with same values in different classes you can setup global for all classes that will be filled.

GenRule rule = GenRule.global(2)
                .add(NameGenerator.class, "name")
                .add(ShortGenerator.class, int.class);

GenFactory factory = new GenFactory(rule);
User user = factory.build(User.class);

In this case all fields with name name in all classes will be filled using NameGenerator and same for int fields with ShortGenerator.

Lambda Rule

You can add anonymous generator or lambda generator as run for your GenRules configuration.

final IGenerator<String> generator = () -> ThreadLocalRandom.current().nextBoolean()
        ? "Bob"
        : "Bill";

GenRule rule = GenRule.auto(User.class, 1)
                .add(generator, "name");

GenFactory factory = new GenFactory(rule);
User user = factory.build(User.class);

Annotation Examples

There are a lot of generators available such as IntegerGenerator, LongGenerator, CityGenerator, etc.

All generators can be found in package io.dummymaker.generator.

As well as all annotations such as GenInteger, GenLong, GenCity, etc. can be found in package io.dummymaker.annotation.simple.

Array Annotations

Responsible for filling array data, can be applied as annotation or GenRule. Will be used automatically via @GenAuto or auto GenRule.

public class User {

    @GenArray
    private int[] numbers;
    
    @GenArray
    private int[][] numberArrays;
}

Collection Annotations

Responsible for filling collection data, can be applied as annotation or GenRule. Will be used automatically via @GenAuto or auto GenRule.

public class User {

    @GenList
    private List<Integer> numberList;
    
    @GenSet
    private Set<Integer> numberSet;
    
    @GenMap
    private Map<Integer, String> numberMap;
}

Time Annotation

Responsible for filling time data, can be applied as annotation or GenRule. Will be used automatically via @GenAuto or auto GenRule.

GenTime annotation is used to create time/dateTime/timestamps for field. Automatically identify field time type and generate value for it.

Supported time fields types:

public class User {

    @GenTime
    private Timestamp timestamp;
    
    @GenTime
    private LocalDateTime dateTime;
}

Date Time Formatter

You cas specify formatter to export dates \ times.

public class User {

    @GenTime(formatter = "HH:mm")
    private LocalTime localTime;
    
    @GenTime(formatter = "yyyy-MM HH:mm")
    private LocalDateTime dateTime;
}

Date Time As Unix Time

You can export dates \ times as unix time format.

public class User {

    @GenTime(exportAsUnixTime = true)
    private Timestamp timestamp;
    
    @GenTime(exportAsUnixTime = true)
    private LocalDateTime dateTime;
}

Embedded Annotation

Responsible for filling complex data, can be applied as annotation or GenRule. Will be used automatically via @GenAuto or auto GenRule.

public class User {

    @GenEmbedded
    private User parent;
}

Special Annotations

Export

Information about different exporters can be found here.

Customization

You can extend basic functionality with your own annotations and generators. All infrastructure will support custom generators, annotations, generate factories with no doubt.

You need to:

IGenerator

Simple generator that produce specific value.

Generator can have pattern to register it for @GenAuto discovery.

public class IntegerSmallGenerator implements IGenerator<Integer> {

    private final Pattern pattern = Pattern.compile("age|grade|group", CASE_INSENSITIVE);

    @Override
    public Pattern pattern() {
        return pattern;
    }

    @Override
    public Integer generate() {
        return ThreadLocalRandom.current().nextInt(1, 101);
    }
}

IComplexGenerator

Is used to build complex values for fields, when simple IGenerator implementation is insufficient, Gen annotation require special parameters or when you need to access field.

public class CustomComplexGenerator implements IComplexGenerator {

    @Override
    public Object generate(Annotation annotation, Field field, IGenStorage storage, int depth) {
        final int from = GenInteger.class.equals(annotation.annotationType()) 
                ? ((GenInteger) annotation).from() 
                : 0;
        
        final int to = GenInteger.class.equals(annotation.annotationType()) 
                ? ((GenInteger) annotation).to() 
                : Integer.MAX_VALUE;

        return ThreadLocalRandom.current().nextInt(from, to);
    }

    @Override
    public Object generate() {
        return new IntegerGenerator().generate();
    }
}

Gen Custom Annotation

For custom IGenerators or IComplexGenerators that don’t require special annotation fields just use @GenCustom annotation that is provided by library to mark fields with your custom generators.

public class User {

    @GenCustom(CustomComplexGenerator.class)
    private String number;
}

Gen Complex Annotation

Is required when you need properties for your IComplexGenerator.

@ComplexGen(CustomComplexGenerator.class)
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface GenInteger {

    int from() default Integer.MIN_VALUE;

    int to() default Integer.MAX_VALUE;
}

In case you want custom annotation for simple generator you can do it as well, just use @PrimeGen instead of @ComplexGen to mark your annotation.

License

This project licensed under the MIT - see the LICENSE file for details.