Creating a Hierarchical Test Data Builder using Generics

At my current client, we had a situation where we needed to write JUnit tests for REST services which used a complex object hierarchy. Creating these objects manually proved to be very tedious, so having learned about using the Test Data Builder Pattern we wanted to make the builders match the object hierarchy.

Here’s an small example of what that hierarchy looks like:

Here’s what the AbstractRelationship Java code looks like:

Because this hierarchy is generated by JAXB, there are no generics in it, so even though as a rule, RelationshipType1 takes RoleType1 as the requester and RoleType2 as the provider, they show up as AbstractRole in the IDE code assist.

One possible solution is to use XML that is generated by the service that we could unmarshal with JAXB. While that worked in some cases, most of the time it ended up being to much of a hassle to keep the XML files up to date when changes were made to schema or if we needed to make changes to the JUnit tests. Plus the services were not ready when we were writing these tests and trying to write the XML with all its complexities was way too much. So we turned to the builders.

One of the things we wanted to do with the builders is make it so that those who used them didn’t worry about the rules of which roles belong to which relationships, this was done by using generics.

Here’s what the builders look like, first the Builder interface:

Let’s take a look at the AbstractRoleBuilder:

A little explanation is now in order. The goal of this builder hierarchy is to match that of the JAXB generated hierarchy, so the methods that are in the abstract JAXB classes have their matching builder methods in the abstract builder class. The “B” generic type (for Builder) is there so that when “withAbstractRoleID” is called from RoleType1Builder for example, it returns itself rather than AbstractRoleBuilder, which follows the pattern.

Here’s what RoleType1Builder would look like:

Quick note: the link provided above provides more of a “true” builder pattern in that the build method is where the object being created is actually built. I deviated from this by using an “instance” variable for the object being built and created it in the constructor; then the fields on it would get changed by calling the builder methods. I found this to be easier than keeping separate variables for each field and it cut down on the amount code. Most of the time either style works, but in order for the below code to work we’ll have to use the “instance” method.

Next up is the AbstractRelationshipBuilder:

Here we use the two role builders to add the requester and provider roles (typed as “R” and “P”). In this case, since the actual instance variable will be on the concrete type, we use the build method to call the “setRequester” and “setProvider” methods since those are on the AbstractRelationship object. Once again we use the “B” type to define the builder.

Last up is the RelationshipType1Builder:

When you set the requester and provider on the above instance, you’ll need to know which roles belong to which relationships because an IDE code assist on both will show AbstractRole. So as you can see from above, the builder is typed to RelationshipType1, the “B” type is RelationshipType1Builder, and the “R” and “P” types are RoleType1Builder, and RoleType2Builder respectively. Now lets talk a look at how we would use the builder:

Now if we use code assist, it will show the correct builder type for the roles. While I used “TypeN” examples here, in my actual project there were 6 relationship types and 10 role types where all but one or two used different type roles for the requester and provider. And in the role hierarchy there was an additional level that I didn’t put in here. Having these builders made writing tests much easier.

About the Author

Object Partners profile.
Leave a Reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]