This document discusses using Ember CLI Mirage to mock API data for testing and development of single-page applications. Ember CLI Mirage allows defining API endpoints and associated data models to mimic backend services. It provides features like factories for generating mock data, associations between models, and serializers to customize API responses. Mirage can be used to seed test data and override responses to test error handling. It integrates with Ember's testing framework and provides shortcuts for common CRUD operations to simplify API mocking.
Report
Share
Report
Share
1 of 35
More Related Content
Rapid prototyping and easy testing with ember cli mirage
2. Me
• Name: Krzysztof Białek
• Github:
github.com/krzysztofbialek
• Twitter: drwrum
• Company: Rebased
3. SPA and dynamic server
data
• No backend api present yet
• Backend api not yet stable
• Mocking api in acceptance testing
4. Dealing with it - testing
with sinon
let project = JSON.stringify({
project: {
id: 1000,
links: {
devices: '/web_api/projects/1000/devices'
}
}
});
server.respondWith('GET', '/web_api/projects/1000', [200,
{"Content-Type":"application/json"}, project]);
5. Dealing with it - testing
with sinon
/* global server */
import Ember from 'ember';
export default Ember.Test.registerHelper('respondGET', function(app,
url, payload) {
let data = JSON.stringify(payload);
server.respondWith('GET', url, [200, {"Content-
Type":"application/json"}, data]);
});
respondGET('/web_api/projects/1000/devices', {
devices: [
{id: 1, name: "ipad"}
]
});
6. Dealing with it - Mocking with
ember cli http mocks
//server/mocks/author.js
module.exports = function(app) {
var express = require('express');
var authorRouter = express.Router();
var AUTHORS = [
{
id: 1,
name: 'Bad Motha Goose'
},
{
id: 2,
name: 'Concrete Octopus'
}
];
authorRouter.get('/', function(req, res) {
var ids = req.query.ids;
var authors;
if (ids) {
authors = authors.filter(function(b) {
return ids.indexOf(b.id.toString()) > -1;
});
}
else {
authors = authors;
}
res.send({"authors": authors});
});
app.use('/authors', authorRouter);
};
8. What can Ember CLI
Mirage do for you?
• Mocking of api in development
• Mocking endpoints in testing
• With it’s own ORM it can mimic ember-data
behaviour
• Bundled into application
• wraps around pretender.js
12. Data layer
• In memory database
• Models
• Factories
• Serializers
13. Going flexible with
models
// mirage/models/author.js
import { Model } from 'ember-cli-mirage';
export default Model;
// and use it in the route
// mirage/config.js
export default function() {
this.get('/authors', (schema) => {
return schema.authros.all;
});
}
14. Going flexible with
models
// app/routes/some-route
Ember.createObject('author', { name: 'Link', age: 123 })
// payload send with the request to ‘authors’ route
author: {
name: 'Link',
age: 123
}
// mirage/config.js
this.post('/authors', (schema, request) => {
let attrs = JSON.parse(request.requestBody).author;
return schema.authors.create(attrs);
});
23. Factories - making data up
since… not so long ago
// 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)();
},
});
24. Factories - seeding data
in development
// 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 });
};
25. Factories - seeding data
in tests
test('I can view the photos', assert => {
server.createList('photo', 10);
visit('/');
andThen(function() {
assert.equal( find('img').length, 10 );
});
});
27. Acceptance testing -
overriding defaults
test("I see the photo's title on a detail route", assert => {
let photo = server.create('photo', {title: 'Sunset over
Hyrule'});
visit('/' + photo.id);
andThen(() => {
assert.equal( find('h1:contains(Sunset over
Hyrule)').length, 1 );
});
});
28. Acceptance testing - make
sure server was called
test("I can change the lesson's title", assert => {
server.create('lesson', {title: 'My First Lesson'})
visit('/');
click('.Edit')
fillIn('input', 'Updated lesson');
click('.Save');
andThen(() => {
// Assert against our app's UI
assert.equal( find('h1:contains(Updated lesson)').length, 1
);
// Also check that the data was "persisted" to our backend
assert.equal( server.db.lessons[0].title, 'Updated
lesson');
});
});
29. Acceptance testing -
testing errors
test('the user sees an error if the save attempt fails',
function(assert) {
server.post('/questions', {errors: ['There was an error']},
500);
visit('/');
click('.new');
fillIn('input', 'New question');
click('.save');
andThen(() => {
assert.equals(find('p:contains(There was an
error)').length, 1);
});
});
32. Shorthands
// Expanded
this.get('/contacts', ({ contacts }) => {
return contacts.all(); // users in the second case
});
// Shorthand
this.get('/contacts'); // finds type by singularizing url
this.get('/contacts', 'users'); // optionally specify the collection as
second param
// Expanded
this.del('/contacts/:id', ({ contacts }, request) => {
let id = request.params.id;
let contact = contacts.find(id);
contact.addresses.destroy();
contact.destroy();
});
// Shorthand
this.del('/contacts/:id', ['contact', 'addresses']);
33. Easy peasy crud
// Resource
this.resource('contacts'); // available in 0.2.2+
// Equivalent shorthands
this.get('/contacts');
this.get('/contacts/:id');
this.post('/contacts');
this.put('/contacts/:id');
this.patch('/contacts/:id');
this.del('/contacts/:id');
34. Mix real api with fake
endpoints
this.passthrough('/addresses', '/contacts');
this.passthrough('/something');
this.passthrough('/else');
// just some verbs
this.passthrough('/addresses', ['post']);
this.passthrough('/contacts', '/photos', ['get']);
// other-origin
this.passthrough('http://api.foo.bar/**');
this.passthrough('http://api.twitter.com/v1/cards/**');
- using sinon
pretender.js can be used as well
define data and then server mock
or you can use a wrapper to make it easier
need to define it each time
starts express js server
needs node :(
only mocking, no testing
no need to have separate server
can be used in production environment
pretender can be configured with any options from it’s doc
namespace like in adapters
all calls to this url will be responded by mirage
all routes in the config file
namespace only for routes defined later
handlers use common verbs
namespace like in adapters
all calls to this url will be responded by mirage
all routes in the config file
namespace only for routes defined later
handlers use common verbs
nicer for endpoints when we want to get all that is in database
or some particular data as we will see in a bit
sets database at runtime
available generators
schema is the instance of database to freely interact with
model persisted in the database
id assigned so ember-data can do it’s magic
data is persisted in the single session to simulate real life exp
same goes for other verbs, deleting models, retrieving collections searching
finder method
response codes
like different key format that ember may expect with adapter app uses
When you return a model or a collection from a route handler, Mirage serializes it into a JSON payload, and then responds to your Ember app with that payload.
mirage ships with serialisers for jsonapi, rest and active model adapters
it’s important to make the payload in same way like server will
by default it takes all the attributes of the model and wraps it under model name key
let’s assume server sends dasherized keys
ok but we still need to get these responses payload from somewhere, right?
couple of ways to do it
think of them as database table
loaded into mirage db
return arrays of POJOS
scenario js file to define what data should be loaded into db
- it’s not the mirage way and it gets inconvenient very quickly
- access to sequence which is only parameter available in factory
faker
there are more
for example avatar or other images
access to server
create and create lists
mirage is bundles with ember app in testing
db is populated when app is booting
reset after each test
when testing UI we test to see changes but not always that means actually call to server was made.
can be also tested by overriding route and testing against request sent by ember
only valid route for the duration of test
if it overrides something from config file it’s just for the test
- mirage has a lot more to offer so I’ll highlight few other sweet features
- we don’t want to write code we don’t need to
- can be whitelisted or constrained to certain verbs