Version:

Seeding your database

Once you’ve defined your server’s routes, you’ll probably want to seed your database with some initial data. You can use factories or fixtures, or both.

In general Mirage recommends that you use factories, for a few reasons:

  • they’re versatile and give you more control over data creation
  • they make your tests more intention-revealing
  • they’re easier to maintain and change

First, we’ll look at using factories.

Defining factories

If you’ve never used factories before, think of them as a way to make database records. You define factories by creating files under the /mirage/factories directory. The name of the factory, which you reference in your tests, is determined by the filename.

Factories have attributes which can be strings, numbers, booleans or functions:

// mirage/factories/user.js
import Mirage from 'ember-cli-mirage';

export default Mirage.Factory.extend({
  name(i) { return `User ${i}`; },
  age: 20,
  admin: false
});

Functions take one parameter, the sequence number of the object being created. This lets you create records with dynamic attributes.

Each time this factory is used to create an object, it will have an autogenerated id assigned to it, since it will be inserted into the database. So, the objects created from the example factory above will look like

{ id: 0, name: "User 0", age: 20, admin: false }
{ id: 1, name: "User 1", age: 20, admin: false }
{ id: 2, name: "User 2", age: 20, admin: false }

and so on.

Mirage also includes the Faker.js library, which pairs nicely with your factories to make your mock data more realistic:

// mirage/factories/user.js
import Mirage, { faker } from 'ember-cli-mirage';

export default Mirage.Factory.extend({
  firstName() { return faker.name.firstName(); },
  lastName() { return faker.name.lastName(); },
  age() { return faker.list.random(18, 20, 28, 32, 45, 60); }, // .list method added by Mirage
});

Once you’ve defined your factories, you can use them to seed your database via the server.create and server.createList methods.

Using factories in development

To seed your database in development, create the file /app/mirage/scenarios/default.js, and export a function that takes a single argument, server, your Mirage server’s instance. This function will run when Mirage is initialized during development.

A simple scenario may look like this:

// app/mirage/scenarios/default.js

export default function(server) {
  server.createList('post', 10);

  var user = server.create('user', { name: 'Zelda' });
  server.createList('comment', 20, { user_id: user.id });
}

Currently, only the default scenario is supported, though there are plans to support switching between multiple scenarios in development.

Note that this scenario will be ignored during testing. This is so you can change your development data without affecting your tests.

Using factories in testing

During acceptance testing, Mirage’s initializer is run when your Ember app starts up, and server is available in each test.

Mirage starts each test with a clean database. Use create and createList to define your data on a per-test basis:

test('I can view the users', function() {
  server.createList('user', 3);

  visit('/contacts');

  andThen(function() {
    equal( find('p').length, 3 );
  });
});

Learn more about acceptance testing in the next section.

Using fixtures in your factory scenarios and tests

You can also use server.loadFixtures to load fixture files in your default scenario or tests, meaning you can simultaneously leverage fixtures and factories. See the guide for more info.

Relationships with factories

You can also create related data with factories. Until this PR lands, you’ll need to manage the foreign keys yourself, which you’ll do by overriding your factories’ default attributes. Here’s an example:

var user = server.create('user');
server.createList('post', 10, { user_id: user.id });

Notice that create and createList return the database records that were created by the factory, so you can use the user’s id to relate the posts back to that user. Now that the foreign keys are set up, you can use the shorthand routes to sideload the user’s related data:

this.get('/users/:id', ['user', 'posts']);

or interact with the data directly via db.posts.where({ user_id: user.id }), for example.


Fixtures

You can also choose to use fixtures instead of (or in addition to) factories. If you’ve never used fixtures before, think of a fixture file as a database table. To add data to your users table, for instance, create the file /app/mirage/fixtures/users.js:

// app/mirage/fixtures/users.js
export default [
  { id: 1, name: 'Link' },
  { id: 2, name: 'Zelda' },
  { id: 3, name: 'Epona' }
];

Fixture filenames are always plural, and export arrays of POJOs. During development, this data will be added to Mirage’s database and will be available in route handlers via db.users. Be sure to delete the /mirage/scenarios directory in order for your fixtures to load during development.

Some people use fixtures during development but choose to use factories in their tests. If you want your fixture files to also load during testing, simply delete the /mirage/factories directory.

Relationships with fixtures

To create related data, you’ll need to manage the foreign keys yourself. Let’s look at an example.

Suppose a user has many addresses. Your fixture data may look like this:

// app/mirage/fixtures/users.js
export default [
  { id: 1, name: 'Link' },
  { id: 2, name: 'Zelda' }
];

// app/mirage/fixtures/addresses.js
export default [
  { id: 1, name: '123 Hyrule Way', user_id: 1 },
  { id: 2, name: '11 Kokiri Forest St.', user_id: 1 },
  { id: 3, name: '5 Lost Woods Dr.', user_id: 2 }
];

Now you can use the shorthand this.get('/users/:id', ['user', 'addresses'], and Mirage will return the user whose id matches the :id route param, along with this user’s related addresses - i.e., addresses whose user_id matches the param.