A Java framework for creating objects as test data (Fixture)
Java
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src
.gitignore
.travis.yml
LICENSE.md
README.md
pom.xml

README.md

FactoryDuke

Build Status Maven Central Coverage Status License

FactoryDuke is a java framework that lets you create objects as test data. All you have to do is define the templates for each of the classes that you want FactoryDuke to create objects from. After that, FactoryDuke takes care of the rest.

Have you ever heard of factory_girl a super cool Ruby framework? Well, FactoryDuke is pretty similar in its use.

Factory Duke is using a lot of lamdba, so it is only compatible with java 8 and higher.

Concept

  • Simple & Fluent
  • Type safe
  • Fast (no reflection)
  • Very light (Only one dependency)

Installing

Use it like a maven dependency on your project

<dependency>
    <groupId>com.github.regis-leray</groupId>
    <artifactId>factory-duke</artifactId>
    <version>0.8</version>
</dependency>

How do we use this?

FactoryDuke is a singleton object where you can register all of the templates. For example, you can define a template as follows:

FactoryDuke.define(User.class, u -> {
            u.setLastName("Scott");
            u.setName("Malcom");
            u.setRole(model.Role.USER);
        });

after you can use this definition

User user = FactoryDuke.build(User.class).toOne();

You can override some fields during the creation of the instance

User user = FactoryDuke.build(User.class, u -> u.setRole(Role.ADMIN)).toOne();

If you need a full control of the bean creation you can return the instance itself.

FactoryDuke.define(User.class, () -> {
            User u = new User();
            u.setLastName("Scott");
            u.setName("Malcom");
            u.setRole(model.Role.USER);
            return u;
        });

You can use factory inside a definition by using the supplier approach, which provide you the ability to extends Factory

FactoryDuke.define(User.class, "admin_user", () -> {
            //create instance from the default definition of User.class
            User u = FactoryDuke.build(User.class).toOne();
            u.setRole(model.Role.ADMIN);
            return u;
        });
User user = FactoryDuke.build(User.class, "admin_user").toOne();

One Factory == One Java File

It is highly recommended that you have one factory for each class that provides the simplest set of attributes necessary to create an instance of that class.

How to create a template factory

public class UserFactory implements TFactory {

    @Override
    public void define(){
        FactoryDuke.define(User.class, u -> {
                u.setLastName("Scott");
                u.setName("Malcom");
                u.setRole(model.Role.USER);
            });
    }
}

Use load() to load all factories definitions from the default locations : classpath:Factories, classpath:factories/**,

FactoryDuke.load();

You can specify you own package

FactoryDuke.load("x.x.custom.package");

Example of loading templates with JUnit tests

@Before
public void setUp() {
    FactoryDuke.load();
}

The templates are save in a static map, if you want to clear all the templates you need to call

FactoryDuke.reset();

Build list of object

Create a list / set of two exact same user

List<User> list = FactoryDuke.build(User.class).times(2).toList();

Set<User> sets = FactoryDuke.build(User.class).times(2).toSet()

If you need to use generator

// the default generator is starting at 1 and incrementing by 1
SequenceValuesGenerator<Long> ids = Generators.sequence();

// constant will return always the same sequence of values
SequenceValuesGenerator<String> names = Generators.values("Scott","John","Malcom");

FactoryDuke.define(User.class, "generator_users", u -> {
    //each generator's id call will return a unique value (1,2,3,4,5, ...)
    u.setId(ids.nextValue());
    //each generator's name call will return a value ("Scott","John","Malcom","Scott","John","Malcom", ...)
    u.setName(names.nextValue());
});

// will return 3 differents users
List<User> users = FactoryDuke.build(User.class, "generator_users").times(3).toList();

Global Hooks (after / before)

If you need to setup a share behavior(s) between cross factories definitions you can now register hook(s).

This hook(s) will be called before or/and after the build() creation.

FactoryDuke.load().addAfterHook(System.out::println)

Also there is a way to disable the global callback for each object build

FactoryDuke.build(User.class).skipAfterHook(true).toOne();

Tips the global callback can be really usefull is you need to implement a persistence layer on specific Object (example with hibernate)

@Inject
private SessionFactory sessionFactory;

@Before
public void loadAndCustomCallback(){
    FactoryDuke.load().addAfterHook(o ->{
        if(o.getClass().isAnnotationPresent(Entity.class)){
            sessionFactory.getCurrentSession().save(o);
        }
    });
}

Licence

FactoryDuke is distributed under the MIT licence