Joist 1.3.0 added some improvements to it’s builders: defaults and fluent methods for codes.
Defaults
Defaults provide a way to quickly create an object, or tree of objects, for testing when you’re not as concerned about every single little value, and want your test to focus on just what is important.
For example, let’s take an object, Employee
, which has required fields for name, description, and start date. Previously you’d have to do:
public void testSomething() {
EmployeeBuilder e = aEmployee()
.name("e") // not really important
.description("d") // not really important
.startDate(CalendarDate.from(2000, 1, 1));
commitAndReOpen();
// do something with the employee
}
However, if your test is only interested in start dates, setting the name and description are a distraction.
So, starting with Joist 1.3.0, you can do something like:
public void testSomething() {
EmployeeBuilder e = aEmployee()
.startDate(CalendarDate.from(2000, 1, 1))
.defaults();
commitAndReOpen();
// do something with the employee
}
The defaults
method (which is generated by Joist) will fill in default values for all required fields. Obviously, it does this very naively—the output is basically:
public EmployeeBuilder defaults() {
if (name() == null) {
name("name");
}
if (description() == null) {
description("description");
}
// will also include default values for field types
if (ageInYears() == null) {
ageInYears(0);
}
if (salaryAmount() == null) {
salaryAmount(Money.dollars(0.00));
}
return this;
}
Recursive Defaults
So, primitives are easy enough, what’s also cool is that defaults
will also fill in any required entities.
For example, if you have an Employer
class, which is required for the Employee.employer
field, but for your current test, you don’t really care about the employer, you just want it there, you can still just call defaults()
:
public void testSomething() {
EmployeeBuilder e = aEmployee()
.startDate(CalendarDate.from(2000, 1, 1))
.defaults();
// employer was filled in
assertThat(e.employer(), is(not(nullValue()));
commitAndReOpen();
// do something with the employee
}
The code that Joist generates looks something like:
public EmployeeBuilder defaults() {
if (employer() == null) {
employer(aEmployer().defaults());
}
// other required fields...
return this;
}
So, note that it calls aEmployer().defaults()
, which means the Employer
instance will have it’s own required fields fill in.
Which means it should be super-easy to get a “basically valid” domain object, regardless of which level of your domain object hierarchy you’re testing.
This has the potential to be a big win, because usually you have to start building domain object hierarchies from the top-down, and the upper levels are rarely meaningful to the test you’re currently writing.
Customizing Defaults
While Joist tries to generate sensible defaults, it knows nothing about your domain model or validation logic, so may very well fill invalid values.
If you need to customize any of the defaults, you can override the generated defaults
method with your own:
// Note: EmployeeBiulder is written by you, while the base
// class EmployeeBuilderCodegen is always generated by Joist
public class EmployeeBuilder extends EmployeeBuilderCodegen() {
@Override
public EmployeeBuilder defaults() {
// say we have a rule that employee age != 0, so the
// super.defaults() behavior of setting any int to 0
// causes validation errors
if (age() == null) {
age(30);
}
// the other defaults are fine
return super.defaults();
}
}
So, you can provide your own logic, but still reuse most of the generated defaults
logic.
Fluent Code Methods for Builders
Builders also got a small, but nice, upgrade in their API for codes.
(Briefly, codes in Joist are basically enums, e.g. an EmployeeType
enum with values of PART_TIME
and FULL_TIME
.)
So, given this EmployeeType
, if you are configuring an Employee
:
public void testEmployee() {
// previously had to do:
aEmployee().employeeType(EmployeeType.PART_TIME);
// can now do:
aEmployee().partTime();
}
This may seem like a small improvement, and, okay, it is, but it can make a noticeable difference in readability when you’re setting up test objects and want to keep the code as succinct and boilerplate-free as possible.
Try It Out
If you haven’t tried out Joist, you should—see the getting started docs and let me know how it goes.