Configurando a simulação de API com Mirage JS e Vue.js

Publicados: 2022-03-10
Resumo rápido ↬ Este artigo apresenta o Mirage JS, uma biblioteca de simulação de API que permite criar, testar e compartilhar um aplicativo JavaScript completo e funcional sem precisar depender de nenhuma API ou serviços de back-end. Você também aprenderá a configurar o Mirage JS com a estrutura de front-end progressivo, Vue.js.

Na era do SPA e do JAMstack, sempre houve uma separação de preocupações entre as APIs e o desenvolvimento front-end. Quase todos os projetos JavaScript que podem ser encontrados na natureza interagem com um serviço da Web ou API e o usam para autenticações ou para obter dados relacionados ao usuário.

Portanto, sempre que você estiver trabalhando em um projeto e a API necessária ainda não tiver sido implementada pela equipe de back-end ou precisar testar rapidamente um recurso, você terá algumas das seguintes opções:

  • Você pode fazer proxy para uma versão de execução local do seu back-end real que, na maioria dos casos, como desenvolvedor front-end, você não teria.
  • Você pode comentar a solicitação real e substituir por dados simulados. (Isso é bom, mas não tão bom quanto você precisaria desfazer isso para chegar à produção e talvez não consiga lidar com estados de rede e latência.)

O que é simulação de API?

A simulação de API é uma imitação ou simulação de uma API real. Isso é feito principalmente para interceptar solicitações que deveriam ser feitas para uma API de back-end real, mas essa simulação existe em seu front-end.

Por que a simulação de API é importante

A simulação de API é significativamente importante de várias maneiras:

  1. Isso cria uma experiência de desenvolvimento front-end muito boa para não depender de APIs de produção antes de criar recursos.
  2. Você poderia compartilhar todo o seu front-end e funcionaria sem depender de uma API de back-end real.
Mais depois do salto! Continue lendo abaixo ↓

O que é Mirage JS?

O Mirage JS foi criado há 5 anos e foi praticamente usado na comunidade Ember antes de Sam Selikoff anunciar oficialmente seu lançamento em 24 de janeiro de 2020 no Twitter.

O Mirage JS resolve o problema de testar APIs de back-end sem depender dessas APIs. Ele permite uma experiência de desenvolvimento front-end perfeita, simulando APIs de produção.

Mirage JS é uma biblioteca de simulação de API para frameworks Vue.js, React, Angular e Ember

O que torna o Mirage JS uma escolha melhor?

Houve outras opções para simulação de API (como interceptores Axios, servidor JSON da Typicode e assim por diante), mas o que acho bastante interessante sobre o Mirage é que ele não atrapalha seu processo de desenvolvimento (como você veria quando o configuramos com o Vue daqui a pouco). É leve e ainda poderoso.

Ele vem com bateria incluída pronta para uso que permite replicar cenários reais de consumo de API de produção, como simular uma rede lenta com sua opção de tempo.

Introdução ao Mirage JS e Vue.js

Então, agora que você sabe o que é Mirage JS e por que é importante para seu fluxo de trabalho de desenvolvimento front-end, vamos ver como configurá-lo com a estrutura da Web progressiva: Vue.js.

Criando um projeto Vue de campo verde (instalação limpa)

Usando a Vue CLI, crie um novo projeto Vue.js entrando no diretório em que você deseja que o projeto seja criado e executado (no seu terminal):

 vue create miragejs-demo-vue

O comando acima configuraria um novo projeto Vue no qual você agora pode cd e executar yarn serve ou npm run serve .

#Instalando Mirage JS

Agora vamos instalar o Mirage JS como uma dependência de desenvolvimento em nosso projeto Vue.js executando o seguinte comando:

 yarn add -D miragejs

Ou se você estiver usando o NPM, execute isto:

 npm install --save-dev miragejs

E é isso! O Mirage JS agora está instalado em nosso projeto Vue.js.

Vamos zombar de algo

Com o Mirage JS instalado, vamos ver como o configuramos para falar com o Vue e simular uma API básica de todos (uma API que retorna uma lista de todos).

Defina seu servidor

Para começar, precisamos criar um arquivo server.js no diretório /src do nosso projeto Vue.js. Depois disso, adicione o seguinte:

 import { Server, Model } from 'miragejs' export function makeServer({ environment = "development" } = {}) { let server = new Server({ environment, models: { todo: Model, }, seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) }, routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) }, }) return server }

Código explicado

Em primeiro lugar, o arquivo server.js é como você configura o Mirage JS para criar uma nova instância de seu servidor fictício (falso) que interceptará todas as chamadas de API que você fizer em seu aplicativo que correspondam às rotas que você definir.

Agora, concordo que o acima pode ser esmagador no início, mas vamos dar uma olhada no que está acontecendo aqui:

 import { Server, Model } from 'miragejs'

A partir do trecho de código acima, estamos importando Server e Model de miragejs .

  • Server
    Esta é uma classe exposta pelo Mirage para nos ajudar a instanciar uma nova instância de um servidor Mirage JS para “servir” como nosso servidor falso.
  • Model
    Outra classe exposta pelo Mirage para ajudar na criação de modelos (um modelo determina a estrutura de uma entrada de banco de dados Mirage JS) alimentada pelo ORM do Mirage.
 export function makeServer({ environment = "development" } = {}) {}

O acima basicamente exporta uma função chamada makeServer do src/server.js . Você também pode observar que estamos passando um parâmetro de ambiente e definindo o modo de ambiente do Mirage para development (você nos veria passando o ambiente de teste mais adiante neste artigo).

O corpo do makeServer

Agora estamos fazendo algumas coisas no corpo do makeServer . Vamos dar uma olhada:

 let server = new Server({})

Estamos instanciando uma nova instância da classe Server e passando para ela uma opção de configuração. O conteúdo das opções de configuração ajuda a configurar o mirage:

 { environment, models: { todo: Model, }, seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) }, routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) }, }

Primeiramente estamos passando o parâmetro de environment que inicializamos na definição da função.

 models: { todo: Model, },

A próxima opção, que é a opção de models , pega um objeto dos diferentes modelos que queremos que o Mirage zombe.

No exemplo acima, nós simplesmente queremos um modelo todo que estamos instanciando da classe Model.

 seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) },

A próxima opção é o método seeds que recebe um parâmetro chamado server . O método de sementes ajuda a criar sementes (sementes são dados iniciais ou uma entrada no banco de dados do Mirage) para nossos modelos. No nosso caso para criar as sementes para o modelo todo fazemos:

 server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" })

então o servidor tem um método create que espera como primeiro argumento uma string que corresponde ao nome do modelo, depois um objeto que conterá as propriedades ou atributos de uma determinada semente.

 routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) },

finalmente, temos o método de rotas que define as várias rotas (as rotas são nossos endpoints de API simulados) O Mirage JS vai simular. Vejamos o corpo do método:

 this.namespace = "api"

esta linha configura o namespace para todas as rotas, o que significa que nossa rota de tarefas agora pode ser acessada de /api/todos.

 this.get("/todos", schema => { return schema.todos.all() })

O acima cria uma rota get e seu manipulador usando o método this.get() . O método get() espera a rota, ou seja, “/todos” e uma função manipuladora que recebe o schema como argumento. O objeto de esquema é como você interage com o ORM do Mirage, que é desenvolvido pelo banco de dados na memória Mirage JS.

Finalmente:

 return schema.todos.all()

Estamos retornando uma lista de todos os nossos todos, usando o objeto de esquema possibilitado pelo ORM do Mirage.

src/main.js

Então terminamos de configurar o src/server.js mas o Vue não sabe sobre isso (pelo menos ainda não). Então, vamos importá-lo em nosso arquivo main.js assim:

 import { makeServer } from "./server"

Então chamamos a função makeServer assim:

 if (process.env.NODE_ENV === "development") { makeServer() }

O acima, if condicional, é um guarda para garantir que o mirage seja executado apenas em desenvolvimento.

Configuração Completa!

Agora configuramos o Miragejs com o Vue. Vamos vê-lo em ação. Em nosso arquivo App.vue , limpamos o conteúdo e substituímos pelo snippet abaixo:

 <template> <ul> <li v-for="todo in todos" v-bind:key="todo.id">{{ todo.content }}</li> </ul> </template> <script> export default { name: 'app', data() { return { todos: [] } }, created() { fetch("/api/todos") .then(res => res.json()) .then(json => { this.todos = json.todos }) } } </script>

Se você estiver familiarizado com o Vue.js, o acima não seria novidade, mas para ser total, o que estamos fazendo é fazer uma solicitação de API usando fetch quando nosso componente App.vue é criado, então passamos os dados retornados para o array todos em nosso estado de componente. Depois, estamos usando um v-for para iterar o array todos e exibindo a propriedade content de cada todo.

Onde está a parte Mirage JS?

Se você perceber, em nosso componente App.vue, não fizemos nada específico para o Mirage, estamos apenas fazendo uma chamada de API como faríamos normalmente. Esse recurso do Mirage é realmente ótimo para o DX porque, nos bastidores, o Mirage interceptaria qualquer solicitação que correspondesse a qualquer uma das rotas definidas em src/server.js enquanto você estivesse em desenvolvimento.

Isso é bastante útil porque nenhum trabalho seria necessário de sua parte para alternar para um servidor de produção real quando você estiver em um ambiente de produção, desde que as rotas correspondam aos pontos de extremidade da API de produção.

Portanto, reinicie seu servidor de desenvolvimento Vue via yarn serve para testar o Mirage JS.

Você deve ver uma lista de dois todos. Uma coisa que você acharia muito interessante é que não precisamos executar um comando de terminal para iniciar o Mirage porque ele remove essa sobrecarga executando como parte de seu aplicativo Vue.js.

Mirage JS e Vue test-utils

Se você já estiver usando o Vue Test-utils em seu aplicativo Vue, achará interessante saber que o Mirage pode trabalhar facilmente com ele para simular solicitações de rede. Vamos ver um exemplo de configuração usando nosso aplicativo todos.

Estaríamos usando Jest para nossos testes de unidade. Então, se você está acompanhando, você pode usar o Vue CLI para instalar o plugin @vue/unit-jest assim:

 vue add @vue/unit-jest

O acima instalará as dependências de desenvolvimento @vue/cli-plugin-unit-jest e @vue/test-utils enquanto também cria um diretório de tests e um arquivo jest.config.js . Ele também adicionará o seguinte comando em nossa seção de scripts package.json (bem legal):

 "test:unit": "vue-cli-service test:unit"

Vamos Testar!

Atualizaríamos nosso App.vue para ficar assim:

 <!-- src/App.vue --> <template> <div v-if="serverError" data-test> {{ serverError }} </div> <div v-else-if="todos.length === 0" data-test> No todos! </div> <div v-else> <ul> <li v-for="todo in todos" v-bind:key="todo.id" :data-test > {{ todo.content }} </li> </ul> </div> </template> <script> export default { name: "app", data() { return { todos: [], serverError: null, } }, created() { fetch("/api/todos") .then(res => res.json()) .then(json => { if (json.error) { this.serverError = json.error } else { this.todos = json.todos } }) }, } </script>

Nada realmente épico está acontecendo no trecho acima; estamos apenas estruturando para permitir o teste de rede que implementaríamos com nosso teste de unidade.

Embora o Vue CLI já tenha adicionado uma pasta /tests para nós, acho que é uma experiência muito melhor quando meus testes são colocados perto dos componentes que estão testando. Então crie uma pasta /__tests__ em src/ e crie um arquivo App.spec.js dentro dela. (Esta também é a abordagem recomendada por Jest.)

 // src/__tests__/App.spec.js import { mount } from "@vue/test-utils" import { makeServer } from "../server" import App from "../App.vue" let server beforeEach(() => { server = makeServer({ environment: "test" }) }) afterEach(() => { server.shutdown() })

Então, para configurar nosso teste de unidade, estamos importando o método mount de @vue/test-utils , importando o servidor Miragejs que criamos anteriormente e finalmente importando o componente App.vue .

Em seguida, estamos usando a função de ciclo de vida beforeEach para iniciar o servidor Mirage JS ao passar no ambiente de teste. (Lembre-se, definimos por padrão o ambiente como development .)

Por fim, estamos encerrando o servidor usando server.shutdown no método afterEach do ciclo de vida.

Nossos testes

Agora vamos detalhar nosso teste (estaríamos adotando a seção de início rápido dos documentos do Mirage js. Assim, seu App.spec.js finalmente ficaria assim:

 // src/__tests__/App.spec.js import { mount } from "@vue/test-utils" import { makeServer } from "./server" import App from "./App.vue" let server beforeEach(() => { server = makeServer({ environment: "test" }) }) it("shows the todos from our server", async () => { server.create("todo", { id: 1, content: "Learn Mirage JS" }) server.create("todo", { id: 2, content: "Integrate with Vue.js" }) const wrapper = mount(App) // let's wait for our vue component to finish loading data // we know it's done when the data-testid enters the dom. await waitFor(wrapper, '[data-test]') await waitFor(wrapper, '[data-test]') expect(wrapper.find('[data-test]').text()).toBe("Learn Mirage JS") expect(wrapper.find('[data-test]').text()).toBe("Integrate with Vue.js") }) it("shows a message if there are no todo", async () => { // Don't create any todos const wrapper = mount(App) await waitFor(wrapper, '[data-test]') expect(wrapper.find('[data-test]').text()).toBe("No todos!") }) // This helper method returns a promise that resolves // once the selector enters the wrapper's dom. const waitFor = function(wrapper, selector) { return new Promise(resolve => { const timer = setInterval(() => { const todoEl = wrapper.findAll(selector) if (todoEl.length > 0) { clearInterval(timer) resolve() } }, 100) }) } afterEach(() => { server.shutdown() })

Nota : Estamos usando um auxiliar aqui (conforme definido nos documentos do Mirage JS). Ele retorna uma promessa que nos permite saber quando os elementos que estamos testando já estão no DOM.

Agora execute yarn test:unit .

Todos os seus testes devem passar neste ponto.

Testando diferentes estados de servidor com Mirage JS

Poderíamos alterar nosso servidor Mirage JS para testar diferentes estados de servidor. Vamos ver como.

 // src/__tests__/App.spec.js import { Response } from "miragejs"

Primeiro, importamos a classe Response do Mirage, depois criamos um novo cenário de teste assim:

 it("handles error responses from the server", async () => { // Override Mirage's route handler for /todos, just for this test server.get("/todos", () => { return new Response( 500, {}, { error: "The database is taking a break.", } ) }) const wrapper = mount(App) await waitFor(wrapper, '[data-test]') expect(wrapper.find('[data-test]').text()).toBe( "The database is taking a break." ) })

Execute seu teste e tudo deve passar.

Conclusão

Este artigo teve como objetivo apresentar o Mirage JS e mostrar como ele contribui para uma melhor experiência de desenvolvimento front-end. Vimos o problema que o Mirage JS criou para resolver (construindo front-end pronto para produção sem nenhuma API de back-end real) e como configurá-lo com Vue.js.

Embora este artigo tenha arranhado a superfície do que o Mirage JS pode fazer, acredito que seja o suficiente para você começar.

  • Você pode acessar os documentos e ingressar no servidor de discórdia do Mirage JS.
  • O repositório de suporte para este artigo está disponível no GitHub.

Referências

  • Os Documentos da Miragem
  • Guia rápido do Mirage Vue