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 for your models. 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. Let’s define a factory for our author model:

ember g mirage-factory author

This creates a factory file for us. Let’s add some attributes:

// mirage/factories/author.js
import { Factory } from 'ember-cli-mirage';

export default Factory.extend({
  name(i) {
    return `Author ${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: "Author 0", age: 20, admin: false}
{id: 1, name: "Author 1", age: 20, admin: false}
{id: 2, name: "Author 2", age: 20, admin: false}

and so on.

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

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

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

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, use the mirage/scenarios/default.js file that was created for you when you installed Mirage. A simple scenario may look like this:

// mirage/scenarios/default.js
export default function(server) {
  server.createList('blog-post', 10);

  let author = server.create('author', {name: 'Zelda'});
  server.createList('blog-post', 20, { author });
}

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 you’ll have access to a server variable within each test. Each test will start with a clean database, so you can use create and createList to define your data on a per-test basis:

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

  visit('/contacts');

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

Using scenarios in testing

If you want to run the same scenario in your tests that runs in development, you can import a scenario and run your server through it:

import defaultScenario from '../../mirage/scenarios/default';

test('I can view the authors', function() {
  defaultScenario(server);

  visit('/contacts');

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

Learn more about acceptance testing in the next section.

Overriding factory attributes

create and createList use the attributes you’ve defined on your factory to create the model; however, it’s often convenient to override these attributes at the time of creation.

To override attributes, pass in an object as the last argument to create or createList with the attributes you want to override. For instance, if we had the following factory definition

// mirage/factories/author.js
import { Factory } from 'ember-cli-mirage';

export default Factory.extend({
  age: 20,
  admin: false
});

then by default authors created from this factory would have an admin attribute of false and an age of 20:

let author = server.create('author');
author.admin // false
author.age   // 20

If we wanted to make an admin, we could do the following:

let author = server.create('author', { admin: true });
author.admin // true
author.age   // 20

We can also override multiple attributes:

let author = server.create('author', { admin: true, age: 30 });
author.admin // true
author.age   // 30

Relationships with factories

You can also create related data with factories. Assuming you have the relationships defined on your models, you can pass in related data as an override:

let author = server.create('author');
server.createList('post', 10, { author });

Now that the foreign keys are set up, your models, shorthand routes and serializers should work as expected.


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 authors table, for instance, create the file /mirage/fixtures/authors.js:

// /mirage/fixtures/authors.js
export default [
  {id: 1, firstName: 'Link'},
  {id: 2, firstName: 'Zelda'},
  {id: 3, firstName: '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.authors.

Use the server.loadFixtures() API to load some or all of your fixture files in development (by calling it from scenario/default.js) or from within a test. See the guide for more info.

Relationships with fixtures

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

Suppose an author has many blogPosts, and each blogPost belongs to an author. Your fixture data may look like this:

// /mirage/fixtures/authors.js
export default [
  { id: 1, firstName: 'Link', blogPostIds: [ 1, 2 ] },
  { id: 2, firstName: 'Zelda', blogPostIds: [ 3 ] }
];

// /mirage/fixtures/blog-posts.js
export default [
  { id: 1, title: 'Lorem', authorId: 1 },
  { id: 2, title: 'Ipsum', authorId: 1 },
  { id: 3, title: 'Dolor', authorId: 2 }
];

You should now know how to seed your database with data, both during development and testing! Let’s wrap up with a look at how to use your shiny new client-side server to write some acceptance tests for your Ember app.