Replace your test fixtures with builders
Tags: programming, testing, javascript, architecture
Published: 2022-07-07 16:55:04

I know most of us are not the biggest fans of writing unit tests for our code. We just want to write our awesome, bug free code and ship it fast! As ideal as this sounds, it’s not realistic and bugs will happen sooner or later - especially if other developers are working on your code without much context and breaks something.

We can all agree that unit testing can get tedious and take forever to write, especially when you have to manually defined your test data. Test data can come in all different shapes and sizes but many people are most familiar with `test fixtures`.

Test fixtures are essentially example data objects that can be used in many instances of your unit tests. In Javascript, you’ll find them typically in a `.json` file or a Javascript file as an object `{}` type.

As your application grows, so will your test fixtures with different variations of the same object type. This can create many obscure one-off fixtures that may be difficult to modify and fit your specific test needs.

### Object Mother

The [Object Mother](http://wiki.c2.com/?ObjectMother) is one pattern that can help solve this problem. This pattern extracts these fixtures in a more factory-like pattern.

```jsx
var testOrder = {
  order_id: 123,
  line_items: [{
    line_item_id: 888,
    product_uuid: 'aaa-bbb-111-222'
  }],
  total: '10.99',
}

/* becomes */

var createSmallOrder = () => ({
  order_id: 123,
  line_items: [{
    line_item_id: 888,
    product_uuid: 'aaa-bbb-111-222'
  }],
  total: '10.99',
})

var createLargeOrder = () => ({
  order_id: 123,
  line_items: [{
    line_item_id: 888,
    product_uuid: 'aaa-bbb-111-222'
  }, 
  /* redacted for brevity */
  {
    line_item_id: 101010,
    product_uuid: 'aaa-bbb-111-222'
  }],
  total: '100.25',
})

var OrderMother = {
  createSmallOrder,
  createLargeOrder
}
```

This pattern is more useful in class-based programming languages that have unique getters and setters, where one could call a method on the Object Mother and only affect that instance but it doesn’t quite work for Javascript land.

### Builders

This is where we could look towards “builders”. I like to use the word “builders” instead of factory, because factories remind me of having a getter, and more importantly in this case a setter, on the factory object to make any changes to the test data we need for the specific test case. Builders give us a fresh new object where we can override any property and works with immutable objects.

If we look at our previous example

```jsx
var testOrder = {
  order_id: 123,
  line_items: [{
    line_item_id: 888,
    product_uuid: 'aaa-bbb-111-222'
  }],
  total: '10.99',
}

/* this would become */

const baseLineItem = {
  line_item_id: 888,
  product_uuid: 'aaa-bbb-111-222'
}

const baseOrder = {
  order_id: 123,
  line_items: [baseLineItem],
  total: '10.99',
}

const aLineItem = (overrides) => ({
  ...baseLineItem,
  ...overrides,
})

const anOrder = (overrides) => ({
  ...baseLineItem,
  ...overrides,
})
```

This allows us to essentially build our test objects with defaults while being able to override what is important to the test. If the test if checking for the total price to be of a certain value, we can set that value manually and in our tests expect that value to appear

```jsx
test('outputs proper total cost', () => {
  const order = anOrder({ total: '19.99'})
  const value = getOrderTotalFormatted(order)

  expect(value).toBe('$19.99')
})
```

Here we don’t care about what the `order_id` is or what the `line_items` are, we just want to know that the output is depending on what we are passing in and defining explicitly.

### Specialized objects

This is a very simple and broad example but we can even have some builders that act like Object Mothers, in that they are objects for a specific use case.

```jsx
const baseLineItem = {
  line_item_id: 888,
  product_uuid: 'aaa-bbb-111-222'
}

const baseOrder = {
  order_id: 123,
  line_items: [baseLineItem],
  total: '10.99',
}

const aLineItem = (overrides) => ({
  ...baseLineItem,
  ...overrides,
})

const anOrder = (overrides) => ({
  ...baseLineItem,
  ...overrides,
})

const anEmptyOrder = () => anOrder({
  line_items: [],
  total: '0.00',
})
```

Notice that we are using the simple builder here to define `anEmptyOrder` because we only care about setting certain properties and properties like `order_id` is irrelevant to this specialized case.

### Resilient to change

Another powerful aspect of this is that if the `order` object gains a new property, let’s say `shipping_method_id`, then only the `baseOrder` needs that new property and all the builders will now have it!

```jsx
/* redacted for brevity */

const baseOrder = {
  order_id: 123,
  line_items: [baseLineItem],
  total: '10.99',
  shipping_method_id: 3
}

/* redacted for brevity */

const anEmptyOrder = () => anOrder({
  line_items: [],
  total: '0.00',
}) // now this will come with `shipping_method_id: 3` 🙌🏽
```

### Conclusion

I’ve been using builders for the past several years and they have made writing unit tests less painful. I love the fact that I can use a default test object and override just what I need in a way it’s clear when reading the tests what the important details are. I encourage you to give this a shot and hopefully this make writing tests a bit less painful for you as well. #happycoding