Ember CLI Mirage for dummies

Juan Antonio Gómez
shokmaster articles
4 min readJul 9, 2019

A la hora de desarrollar y testear una aplicación, resulta más cómodo no tener que montar todo backend en nuestra máquina local o hacer peticiones a uno remoto. La alternativa es utilizar mocks, y para ello contamos con el addon Ember CLI Mirage, con el que podemos construir una copia de los servicios remotos, con factorías de datos y personalización total de las llamadas y respuestas HTTP.

Configuración

Mirage está activado por defecto en los entornos de development y test. Si preferimos desactivarlo en algún entorno y utilizar el backend remoto, podemos hacerlo en el fichero config/environment.js:

// config/environment.js
...
if (environment === 'development') {
ENV['ember-cli-mirage'] = {
enabled: false
};
}

En el fichero mirage/config.js se configuran los endpoints que Mirage debe interceptar. Por defecto, si se hace una petición a un endpoint que no esté definido en Mirage, se lanzará un error. Este comportamiento puede modificarse con la función passthrough():

// mirage/config.js
this.passthrough();

Esto hará que Mirage deje pasar las llamadas a endpoints que desconoce, e intercepte sólo los que tiene definidos.

Funcionamiento básico

Para ver la siguiente documentación en práctica, os recomiendo leer el artículo Mocking a Blog Backend with Mirage and JSON API.

Para endpoints sencillos, podemos especificar directamente qué respuesta queremos que genere Mirage:

// mirage/config.js
this.get('/posts', () => {
return {
data: [
{ id: 1, type: 'posts', attributes: { title: 'Post 1' } },
{ id: 2, type: 'posts', attributes: { title: 'Post 2' } },
{ id: 3, type: 'posts', attributes: { tiele: 'Post 3' } },
]
};
});

Cada vez que la aplicación haga una llamada GET a /posts, Mirage responderá con este objeto de datos.

Datos dinámicos

Obviamente, los datos hardcoded se nos quedarán cortos para la mayoría de nuestras necesidades. La solución es generar modelos y datos dinámicos.

Creamos un modelo para Mirage:

$ ember g mirage-model post

Ahora configuramos el endpoint dinámico para que utilice la base de datos de Mirage:

// mirage/config.js
this.get('/posts', (schema, request) => {
return schema.posts.all();
});
// o simplemente, haciendo uso del standard:
this.get('/posts');

Pero la base de datos de Mirage aún está vacía, por lo que este endopoint nos devolverá un array vacío. Veamos cómo llenarla de información.

Generando datos de prueba

Mirage utiliza Factories para crear mocks de modelos. Así podemos generar automáticamente datos de prueba para nuestros modelos, en lugar de tener que definirlos a mano. Como ayuda, utilizamos la librería Faker.js.

Vamos a crear un factory para nuestro modelo Posts:

ember g mirage-factory post

Ahora podemos definir de qué manera se generarán los datos para cada propiedad del modelo:

// mirage/factories/post.js
import { Factory } from 'ember-cli-mirage';
import faker from 'faker';
export default Factory.extend({
title(i) {
return `Post ${i} ${faker.lorem.sentence()}`;
},
image(i) {
return `image-${i}`; // image-1, image-2, etc.
},
description() {
return faker.lorem.paragraph();
}
});

Por último, llenamos nuestra base de datos con las funciones server.create (para 1 elemento) o server.createList (para un conjunto de elementos):

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

Ya tenemos nuestro modelo, cómo generar sus datos (factory) y la DB de Mirage llena de información. Ahora, nuestro fake-endpoint /posts devolverá un conjunto de Posts con datos aleatorios que cumplen con las mismas reglas que el backend real.

NOTA: en los tests de aceptación, Mirage ignora el fichero scenarios/default.js. De esta forma comenzamos cada test con un entorno limpio, en el que podemos crear los modelos que necesitemos para cada test (igual que antes, con create() o createList()). Después de cada test, el servidor de Mirage se reinicializa, por lo que el estado final de los datos de un test nunca influirá en el siguiente.

Traits

A veces necesitamos especificar ciertas características del modelo que necesitamos. Por ejemplo, podemos necesitar todos los datos de un Post, pero que además éste haya sido archivado. Aquí intervienen los Traits:

// mirage/factories/post.js
import { Factory, trait } from 'ember-cli-mirage';
export default Factory.extend({
title(i) {
return `Post ${i} ${faker.lorem.sentence()}`;
},
... archived: trait({
archivedAt() {
return faker.date.past();
}
})
});

Con este trait llamado archived, sobreescribimos el valor de la propiedad archivedAt (que por defecto no estaba definida) para que devuelva una fecha pasada, es decir, un Post archivado. La forma de seleccionar este trait es muy simple:

server.create('post', 'archived'); // devuelve 1 post archivado
server.createList('post', 3, 'archived'); // devuelve un array de 3 posts archivados

Relaciones entre modelos

Mirage también nos permite configurar relaciones entre modelos. Por ejemplo, si queremos añadir comentarios a nuestros posts (relación Post -> hasMany -> Comment):

  1. Especificamos el tipo de relación en el modelo Mirage:
// mirage/models/post.js
import { Model, hasMany } from 'ember-cli-mirage';
export default Model.extend({
comments: hasMany(),
});
  1. Le decimos al factory que queremos comments en los posts:
// mirage/factories/post.js
export default Factory.extend({
...
afterCreate(post, server) {
server.createList('comment', 5, { post });
}
});

Con todo lo anterior, ya estamos listos para aprovechar toda la potencia de Mirage. Si utilizáis otros frameworks, sin duda este será uno de los addons que más echaréis de menos de Ember.js.

Saludos!

--

--