Acceptance testing your Ember app typically involves verifying some user behavior. For example, you may want to test that the user can view the photos on your app’s index route.
Many of these tests rely on a given server state. In other words, you want to test that the user can view ten photos, given ten photos exist on the server when the user boots the app. This is where factories come in.
Factories let you define the initial server state directly in the test:
test("I can view the photos", function() {
server.createList('photo', 10);
visit('/');
andThen(function() {
assert.equal( find('img').length, 10 );
});
});
server.createList
looks up the photo
factory (the file /app/mirage/factories/photo.js
), and generates 10 database records from it, using the attributes generated by the factory. This way, Mirage’s database is populated when the Ember app boots and makes an AJAX request to fetch the photos data.
Note that Mirage’s server is restarted after each test. This means the database will start out empty at the beginning of each test.
The purpose of factories is to put code that’s highly relevant to a test as close to that test as possible. In the example above, we wanted to verify that the user would see ten photos, given those photos existed on the server. So, the server.createList('photo', 10)
call was directly in the test.
Say we wanted to test that when the user visits a details route for a photo titled “Sunset over Hyrule,” they would see that title in an <h1>
tag. One way to accomplish this would be to update the photo factory itself:
// app/mirage/factories/photo.js
import Mirage from 'ember-cli-mirage';
export default Mirage.Factory.extend({
title: 'Sunset over Hyrule'
});
The problem with this approach is that the desired change is very specific to this test. Suppose another test wanted to verify photos with different titles were displayed. Changing the factory to suit that case would break this test.
For this reason, create
and createList
allow you to override specific attributes that your factory has defined. This allows us to adhere to the principle of keeping relevant code near our test.
To override attributes, simply pass in an object as the last argument to create
or createList
with the attributes you want to override. Here’s what this may look like for the photos example.
First, the (more generic) factory:
// app/mirage/factories/photo.js
import Mirage from 'ember-cli-mirage';
export default Mirage.Factory.extend({
title(i) { return `Photo ${i}`; } // Photo 1, Photo 2 etc.
});
And now we can write our tests, overriding the factory-generated attributes where appropriate:
test("I can view the photos", function() {
server.createList('photo', 10);
visit('/');
andThen(function() {
assert.equal( find('img').length, 10 );
});
});
test("I see the photo's title on a detail route", function() {
var photo = server.create('photo', { title: 'Sunset over Hyrule' });
visit('/' + photo.id);
andThen(function() {
assert.equal( find('h1:contains(Sunset over Hyrule').length, 1 );
});
});
We override title
in the second test since it’s relevant there, but we stick with the generic factory-generated titles for the first test.
You can use attribute overrides to set up related database records in your tests. Let’s say we want to write a test given that a user with 10 photos exists on the server.
We could set up the relationship like this:
test("I see the photo's title on a detail route", function() {
var user = server.create('user');
server.createList('photo', 10, { user_id: user.id });
// write your test
Now any route that requests this user and their photos will retrieve all the data.
You should now know enough to mock out your JSON API and test your app using Mirage! Check out the next section for a detailed reference on all of Mirage’s features.