Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The Builder Pattern for Test Classes

DZone's Guide to

The Builder Pattern for Test Classes

The great thing about design patterns is how versatile they are. See how one dev used the Builder Pattern to make his unit and integration testing easier and more readable.

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

I've always been interested in software testing, either unit tests, integration tests, UI tests, etc. Not. Not because it's the most fun part of software development, but because I think it's important to deliver quality — and testing is the main way to achieve it.

Recently, I wrote a lot of integration tests for an API with some quite complex object trees. Well, to make effective tests, I need to insert effective data.

In this case, our APIs are generated using Jhipster. The following code snippet shows how a Jhipster application creates and persists objects in integration tests.

createEntity method is generated, being responsible for instantiating a class, eventually calling a createEntity from another test class to create an instance of a dependency.

public class NodeResourceIntTest {

@Autowired
private EntityManager em;

private Node node;

    public static Node createEntity(EntityManager em) {
        node = new Node()
            .lft(DEFAULT_LFT)
            .rgt(DEFAULT_RGT)
            .nodeOnline(DEFAULT_NODE_ONLINE)
            .lastChangeAt(DEFAULT_LAST_CHANGE_AT);

        // Add required entity
        Tree tree = TreeResourceIntTest.createEntity(em);
        em.persist(tree);
        em.flush();
        node.setTree(tree);
        return node;
    }

    @Before
    public void initTest() {
        node = createEntity(em);
    }
}


This example remains simple. What if I tell you that a Tree object is also linked to several children of type Nodes, themselves having a set of MetaGroup, each MetaGroup having a MetaType and a set of Meta — and so on.

Some tests don't need the full hierarchy, but for some others, a full hierarchy is required for the test to be relevant.

A convenient way to create objects is the builder pattern. Creating a Node object could look like this.

Node node = new NodeBuilder()
    .withLft(DEFAULT_LFT)
    .withRgt(DEFAULT_RGT)
    .withNodeOnline(DEFAULT_NODE_ONLINE)
    .withLastChangeAt(DEFAULT_LAST_CHANGE_AT)
    .build();


Good enough for a unit test, but I also do a lot of integration tests and, therefore, I want my objects to be added to my persistence context and stored in my database. Therefore, I added a buildAndPersist method to my builder, and to be able to persist it, I needed an EntityManager, which I pass to the builder via the constructor.

Node node = new NodeBuilder(entityManager)
    .withLft(DEFAULT_LFT)
    .withRgt(DEFAULT_RGT)
    .withNodeOnline(DEFAULT_NODE_ONLINE)
    .withLastChangeAt(DEFAULT_LAST_CHANGE_AT)
    .buildAndPersist();


I ended up building an abstract class so that all my builders would follow the same behavior.

public abstract class AbstractPersistenceTestBuilder<T> {

    private final EntityManager em;

    public AbstractPersistenceTestBuilder(EntityManager em) {
        this.em = em;
    }

    public T buildAndPersist() {
        T target = build(); // Builds the object via the implementation provided by the concrete class
        em.persist(target); // Persists the created object
        return target; // Returns the instance linked to the persistence context
    }

    public abstract T build();
}


From now on, building a complex object can be as simple as this code snippet, which, in my opinion, also makes the code much easier to read.

Tree tree = new TreeTestBuilder(em)
      .withName("firstTree")
      .withCategory("category")
      .withCode(UUID.randomUUID().toString())
      .withLastChangeAt(now())
      .withMetaGroups(newHashSet(new MetaGroupTestBuilder(em)
            .withMetaType(new MetaTypeTestBuilder(em)
                  .withName("title")
                  .withProtectedType(TRUE)
                  .withValueType(MetaTypeValueType.TEXT)
                  .withLastChangeAt(now())
                  .buildAndPersist())
            .withMetas(newHashSet(new MetaTestBuilder(em)
                  .withMetaValue("my lovely title")
                  .withContextLanguage(ContextLanguage.EN)
                  .withLastChangeAt(now())
                  .buildAndPersist()))
            .buildAndPersist()))
      .withLabels(newHashSet(labelLevel))
      .buildAndPersist();


The source code is available on my GitHub.

In my next post, I hope to publish this very simple library to Maven Central as an exercise.

Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:
java ,unit testing ,integration testing ,api testing ,builder pattern ,design patterns ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}