Lessons from Rails: Generate test data

Lessons from Rails: Generate test data
import { lessons } from 'rails'; // part 1

Bringing techniques used in Rails to Node

late
Better late than never. I'm happy, if not surprised, to be learning so much from Rails in 2018.

This is the second post in a series about techniques I found in Rails that were so good, they needed to be ported to my Node projects.

Technique 2 - Generate test data with factories

In the RSpec Better Specs documentation, they say, "Do not use fixtures because they are difficult to control, use factories instead. Use them to reduce the verbosity on creating new data."

This is a simple argument, so here it is.

Setup code in your tests like the following isn't pleasant. Nor is it maintainable.

const user = new User({
  name: 'Joe User',
  email: 'joe@fake.com',
  birthdate: new Date('1999-03-30'),
});
const user2 = new User({
  name: 'Bill Jones',
  email: 'bill@fake.com',
  birthdate: new Date('1973-12-08'),
});
const message1 = new Message({
  content: 'This is the content of message1',
  user: user1,
});
const message2 = new Message({
  content: 'Message2 content!',
  user: user2,
});

Why manually create the boilerplate data needed to instantiate these test entities every time we need them? And each time the API changes for these entities, we'll need to update dozens of references. Thankfully, there are libraries that can help us with this.

How about code like this instead?

const user1 = casual.user;
const user2 = casual.user;
const message1 = casual.message({user: user1});
const message2 = casual.message({user: user2});

This makes for much cleaner, much more maintainable tests.

Casual

In the examples, we used casual, a fake data generator. There are other options in the Javascript ecosystem - Chance looks popular - but I like casual because it has a simple, extensible API.

For the above example, you could create casual definitions like so:

const casual = require('casual'),
  moment = require('moment');

casual.define('user', function () {
  return {
    name: casual.name,
    email: casual.email,
    birthdate: casual.date,
  };
});

casual.define('message', function (opts) {
  return Object.assign({
    content: casual.sentence,
    user: casual.user,
  }, opts);
});

You just need to make sure to put those definitions in a file that is loaded before your tests run. You will then have access to casual.user and casual.message whenever casual is required in your tests.

Wrap it up

So that's it. A pattern that is an accepted norm in the Rails/RSpec community works pretty well in JavaScript. Take the 10 minutes required to set up test data factories and enjoy cleaner, easier to maintain tests!