DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

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.

Anthony Richir user avatar by
Anthony Richir
·
Jan. 24, 18 · Tutorial
Like (6)
Save
Tweet
Share
19.36K Views

Join the DZone community and get the full member experience.

Join For Free

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.

A 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.

Testing Builder pattern

Published at DZone with permission of Anthony Richir. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Using QuestDB to Collect Infrastructure Metrics
  • Public Cloud-to-Cloud Repatriation Trend
  • Project Hygiene
  • Last Chance To Take the DZone 2023 DevOps Survey and Win $250! [Closes on 1/25 at 8 AM]

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: