Efate Test Fixture Generator Series: Extending the library
Tags: javascript, testing
Published: 2022-09-06 03:10:37

---
title: Efate Test Fixture Generator Series: Extending the library
published: true
description: How to extend the library with your own property builder
tags: JavaScript, Testing
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
---

This is a second post in a series discussing test fixture generators and the efate library.  The [first post](https://dev.to/jcteague/make-testing-easier-with-test-fixture-generators-5485) discusses how to use efate. This post discusses how you can extend the efate library.  You might want to give that a read first to give you a bit of background.

[Efate](https://github.com/jcteague/efate) is actually the second test fixture library I've written and there were several lessons I learned after using the first iteration for several years myself.  The first version was influenced a great deal by [factor_girl](https://github.com/thoughtbot/factory_bot) (called factory_bot now), with a heavy dependency on strings to define and create the fixtures.  It also wasn't very modular.  You couldn't just import a specific fixture, you had to bring in the whole library.  And it wasn't very extensible, if you needed to define custom behavior for how a field should be created, it wasn't very pretty.

I was able to solve these problems in efate; it's modular by default, some (I would say) clever Typescript practically eliminates strings in definitions, and it was designed to be extensible, which is the subject of this article.  You can extend the library by adding your own property builder that lets you define how properties in your object graph should be populated

## Creating a Property Builder Plugin
When you define a fixture, you get to determine exactly how each property should be populated, whether it's a string, a date, an email address, property builders are used to generate the value. There are two independent, but linked components that describe the builder and its behavior when populating a field:
1. An interface that describes the builder function when 
2. An object that has a method that matches the name declared in the interface and returns a curried function that generates value at creation time.

### Defining the Property Builder Interface
We'll use the `asDate()` property builder as our example*.  It takes an option during definition to affect its behavior during creation, whether or not the next date is incremented by day.  If not, the default behavior to use `now` as the value of the field.

The first step is to create an interface that describes the method that is used during fixture definition, describing any options that can be expressed during definition.  The `DateBuilder` lets you customize how the date values are created.  The interface is relatively simple:
```TypeScript
export interface DateBuilderOptions {
    incrementDay: boolean;
}
export interface DateBuilderExtension{
    asDate: (options?: DateBuilderOptions) => void;
}
```
<mark>There is no implementation of this interface</mark>, it's just used to define the builder function and what options it provides at fixture definition. My next post will be about efate internals and how this works.

## Create the builder object
Okay, the next part is a bit tricky, but once you see the pattern and understand what it's doing it's not so bad.  You now need an object that matches the _name_ of the function in the Extension interface, but it will return a curried function that returns a `FieldGeneratorFunc`

```TypeScript
type FieldGeneratorFunc = (increment: number) => Field<any>;
// I'm writing these out as named functions,
// but in the source code you will see these as lambda functions
function asDateBuilder(name: string, [options]: [DateBuilderOptions?] = [defaultOptions]){
    return function dateBuilderGenerator(increment: number){
        const date = new Date();
        if (options?.incrementDay) {
          date.setDate(date.getDate() + (increment - 1));
        }
        return new Field(fieldName, date);
    }
}
export const dateBuilderExtension {
    asDate: asDateBuilder
}
```
Okay, so let's break this down a bit. The outer function gets executed during fixture definition. It has one required parameter, `name` that is passed to the function internally when the fixture is defined.  The second parameter is the array of arguments that are passed to the definition function.  As a convention, I use a single options object for this parameter for consistency. However that is not a requirement.

The second function is executed at creation time and must conform to the `FieldGeneratorFunc` type definition.  The fixture tracks how many times you create a fixture internally and will pass that value as the `increment` parameter.  You can choose how to use this parameter, or ignore it.  The function returns a `Field<T>` object, which is just a simple object that tracks the name of the field and it's value.

## Adding your Extension
To Actually extend efate, you need to pass the extension interface as a type parameter and the extension object as a function parameter to the fixture factory.
```TypeScript
import {createFixtureFactory} from 'efate';
// import both the interface and the object for the extension
import {DateBuilderExtension, dateBuilderExtension} from 'DateBuilderExtension'
const createFixture = createFixtureFactory<DateBuilderExtension>(dateBuilderExtension);
//start using your extension to build your fixtures
```
If you have more than one extenstion, you can combine them using a [type intersection](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#intersection-types).
```TypeScript
import {createFixtureFactory} from 'efate';
// import both the interface and the object for the extension
import {DateBuilderExtension, dateBuilderExtension} from 'DateBuilderExtension'
import {UUIDExtension, uuidExtension} from 'efate-uuid';
const createFixture = createFixtureFactory<DateBuilderExtension & UUIDExtension>(dateBuilderExtension, uuidExtension);
//start using your extension to build your fixtures
const userFixture = createFixture<User>(t => {
    t.id.asUUID();
    t.date.asDate();
});
```
Adding the extensible behavior to efate's growing list of internally provided property builders, it should be able to meet any need.  It does gets more complicated as you move away from random fields to a complete object graph. [efate-uuid](https://github.com/jcteague/efate/tree/master/packages/efate-uuid) is an example of a simple extension you can review.  If you come up with a good extension, please consider contributing it to the library.  I accept pull requests!

*_The date builder was the first pass at passing additional options to a builder at design time.  There are lots of other ways someone might want to modify the behavior of date generation. As always suggestions and pull requests are welcome._