Draconian Overlord

Oppressing software entropy

Joist Builder Defaults

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.