November 15, 2010

Builders to setup Unit test data

Exposing Setter methods or constructors in domain objects for setting up Unit test data should never be done.
Builder pattern is a simple alternate to setup unit test data using reflection without exposing unnecessary setters and constructors in domain objects.

1) Abstract builder:
Create an Abstract builder that will be extended by all domain builder objects.
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AbstractBuilder<E> {
    private E domain;

    public AbstractBuilder(E domain) {
        this.domain = domain;
    }

    public E build() {
        try {
            List<Field> domainFields = getAllFields(new ArrayList<Field>(), domain.getClass());
            Map<String, Field> builderFieldsMap = index(getAllFields(new ArrayList<Field>(), this.getClass()));
            for (Field domainField : domainFields) {
                Field builderField = builderFieldsMap.get(domainField.getName());
                if (builderField != null) {
                    copyFieldValue(domainField, builderField);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return domain;
    }

    private void copyFieldValue(Field domainField, Field builderField) throws IllegalAccessException {
        domainField.setAccessible(true);
        builderField.setAccessible(true);
        domainField.set(domain, builderField.get(this));
    }

    private Map<String, Field> index(List<Field> allFields) {
        Map<String, Field> map = new HashMap<String, Field>();
        for (Field allField : allFields) {
            map.put(allField.getName(), allField);
        }
        return map;
    }

    public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
        for (Field field : type.getDeclaredFields()) {
            fields.add(field);
        }
        if (type.getSuperclass() != null) {
            fields = getAllFields(fields, type.getSuperclass());
        }
        return fields;
    }

    public E getDomain() {
        return domain;
    }
}

2) Domain builder class:
Consider a domain class Person with 4 attributes (firstName, lastName, gender, dateOfBirth). You should create the domain builder object with same 4 attributes.

public class PersonBuilder extends AbstractBuilder<Person> {
    private String firstName;
    private String lastName;
    private String gender;
    private Date dateOfBirth;

    public PersonBuilder() {
        super(new Person());
    }

    public PersonBuilder withFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public PersonBuilder withLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

    public PersonBuilder withGender(String gender) {
        this.gender = gender;
        return this;
    }

    public PersonBuilder withDateOfBirth(Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
        return this;
    }
}

3) Using Builders in Unit Tests:

You can use the builder as shown below
Person testPerson = new PersonBuilder().withFirstName("hello").withLastName("World").build();

Tip:
If you are using Intellij IDEA as the IDE, use Builder plugin to generate the "with" methods quickly.

7 comments:

  1. You rock ! Wow. This helped big time in writing my domain tests.
    -Sriram Narasimhan

    ReplyDelete
  2. Thanks for posting....makes creating builders so much less work.

    ReplyDelete
  3. İnstagram takipçi satın al! İnstagram takipçi sitesi ile takipçi satın al sende sosyal medyada fenomen olmaya bir adım at. Sende hemen instagram takipçi satın almak istiyorsan tıkla:

    1- takipçi satın al

    2- takipçi satın al

    3- takipçi satın al

    ReplyDelete
  4. Thanks for all you do. I like the website themes and layout, I follow you with joy and I respect your articles. travel health surveillance form Kenya Is necessary for all the travelers who want to visit Kenya . Because it is compulsory by the government of Kenya .

    ReplyDelete