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:
First, we’ll look at using 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.
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.
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 );
});
});
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.
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
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.
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.
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.