miragejs: generating reasonable data

2021-02-26

 | 

~3 min read

 | 

510 words

MirageJS is an API mocking tool for Frontend developers and came out of the Ember community. It’s pretty awesome.

Not only can you use it to mock endpoints, but you can also set it up generate test data.

I’m still getting comfortable with all of the details around this, but one feature I recently discovered that has been quite helpful is the afterCreate hook that’s available within the Server’s create method.

The after create can be used in a number of ways to link related data. Say, you have multiple data models that are related - a customer has a user, a store has a manager, a shopping cart has items, etc. The afterCreate hook allows for these sorts of linking!

The use case that I had that led me to find afterCreate was actually slightly different. In my case, I was working with an Delivery model which had a status and multiple time stamps.

Because it was test data, for a while, we accepted that it wouldn’t always “make sense.” For example, we had a factory something like this:

originalFactory.ts
export default class DeliveryDetail extends Mirage.Factory.extend({
  status() {
    return faker.random.arrayElement([
      Unknown,
      Pending,
      InProgress,
      Delivered,
      Canceled,
    ]);
  },
  acceptTimeLocal() {
    return valueOrUndefined(faker.date.future());
  },
  cancelTimeLocal() {
    return valueOrUndefined(faker.date.future());
  },
  quotedPickupTimeEstimateLocal() {
    return valueOrUndefined(faker.date.future());
  },
  /*...*/
})

function valueOrUndefined(value) {
  return faker.random.boolean() ? undefined : value;

The issue is that if the order was never canceled, cancelTimeLocal was supposed to be undefined (and similar issues for all of the other time stamps) but our factory didn’t account for that.

This worked for a while, but recently, I needed the data to make sense in order to determine if a new feature was working properly. This led me down a path to discover afterCreate.

The nice thing about afterCreate, is that you have access to the entire model that was just created in addition the entire Server.

The result is that I could do something like this:

reasonableFactory.ts
export default class DeliveryDetail extends Mirage.Factory.extend({
  status() {
    return faker.random.arrayElement([
      Unknown,
      Pending,
      InProgress,
      Delivered,
      Canceled,
    ]);
  },
    /*...*/
  afterCreate(model: DeliveryDetail & {id:number}, server: Server){
      const reasonableTimes = generateReasonableTimes(model);
      Object.assign(model, reasonableTimes)
      server.db.deliveryDetails.update(model.id, model)
      }
})

A few things to call out:

  1. In the factory, Status which means that it’s invoked each time the factory is invoked - ensuring that each value is (potentially) different
  2. The model passed into afterCreate will look just like the model you’ve described with one notable difference: Mirage will inject an id value that’s associated with its row in the database1
  3. In this case we updated an existing record (identified by model.id), but there’s a lot of other possible interactions available with the database. My understanding is this is called DbCollection in Mirage JS.

The bottom line: through the use of the afterCreate hook within my factory, I was able to generate much more reasonable data enabling me to test my code with confidence!

Footnotes

  • 1 It’s unclear to me at this point how this would work if the model includes an id value.

Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!