Storybook é um dos melhores maneiras de desenvolver componentes de IU para aplicativos JavaScript porque permite a visualização de componentes em vários estados, atua como documentação interativa do código, bem como tem um ambiente ao vivo para permitir o desenvolvimento que prioriza a história.
Embora a apresentação de pequenas unidades de IU no Storybook seja simples, quando se trata de componentes que fazem solicitações de API, os desenvolvedores devem buscar uma solução de simulação de API para obter controle sobre as respostas e tirar a comunicação HTTP real da história.
Neste artigo, vamos integrar uma biblioteca de simulação de API chamada Mock Service Worker em um projeto de livro de histórias.
O que é Mock Service Worker?
Mock Service Worker (MSW) é uma biblioteca de simulação de API para navegador e Node.js. Além do amplo suporte de REST e API GraphQL , o principal recurso da biblioteca é a interceptação de solicitações no nível da rede por meio do Service Worker. Isso significa absolutamente nenhuma alteração feita no componente que você está testando ou desenvolvendo, já que ele não tem conhecimento de nenhum tipo de simulação e continua fazendo as mesmas solicitações que faz na produção.
Combinado com o Storybook, o MSW proporciona uma experiência inigualável de desenvolvimento de componentes, fornecendo uma maneira perfeita de controlar a comunicação interna e externa da API. Não é à toa que o MSW é um dos as formas recomendadas para interceptar API no Storybook!
Configurando um livro de histórias e um projeto de trabalhador de serviço simulado
Usaremos um novo projeto Criar aplicativo React . Tanto o Storybook quanto o MSW são ferramentas independentes de estrutura, portanto, você pode usar as etapas deste artigo para integrá-los a qualquer outro projeto JavaScript, seja Angular , Vue.js ou Svelte .
Você pode ver o código-fonte completo do projeto em GitHub .
Instalando o Storybook
Vamos começar instalando o Storybook:
$ npx sb init
Por favor, consulte Primeiros passos página na documentação do Storybook para mais detalhes sobre a instalação.
Assim que o Storybook for instalado, você deverá ver alguns novos diretórios aparecendo em seu projeto:
|-.storybook | |-main.js | |-preview.js |-src | |-/histórias
A seguir, vamos adicionar o pacote msw
:
$ npm install msw--save-dev
Inicializando o Service Worker
O Mock Service Worker usa um script de trabalho que permite a interceptação de solicitações em um navegador. A biblioteca vem com uma CLI designada para inicializar esse script de trabalho automaticamente.
Para inicializar o script de trabalho, execute o comando npx msw init
e forneça a ele um caminho relativo para o diretório público do seu projeto, que no caso de create-react-app, é o ./pasta pública
:
$ npx msw init./public
O diretório público pode diferir dependendo do projeto. Veja a lista de públicos comuns diretórios para referência.
Criando um componente React
Nosso projeto será um componente React que exibe um pequeno detalhe sobre um usuário GitHub. A intenção é renderizar esse componente assim:
Vamos dar uma breve olhada no código-fonte do componente GitHubUser
:
//src/GitHubUser.jsx importar React de'react' import {useFetch} de'../../../hooks/useFetch' import'./GitHubUser.css' export const GitHubUser=({username})=> { //Buscar detalhes do usuário na API GitHub V3. const {dados, carregamento, erro, refetch}=useFetch ( `https://api.github.com/users/$ {nome de usuário}` ) const {nome, login, avatar_url}=dados || {} //Componha algumas classes condicionais com base no estado da solicitação. const containerClassNames=[ 'recipiente', carregando &&'carregando', erro &&'erro', ] .filter (booleano) .Junte-se('') //Eventualmente, renderize alguma marcação. Retorna () }{avatar_url && }{erro? (): (Falha ao buscar um usuário GitHub.
)}{name}
{login}
Para obter os detalhes de um determinado usuário, este componente chama uma API GitHub V3 por meio de um gancho useFetch
personalizado-uma pequena abstração sobre o window.fetch
nativo. Ele também tem uma boa funcionalidade de”nova tentativa”no caso de falha na chamada da API.
Embora seja uma parte válida do comportamento do componente, a solicitação HTTP que ele faz não pertence ao Storybook. Fazer solicitações reais em uma história, especialmente para fornecedores terceirizados, estabeleceria uma forte dependência de nossa IU no respectivo serviço, evitando que as histórias que escrevemos sejam reproduzíveis e desativando o uso offline do Storybook.
Escrevendo uma história
Como estamos nos concentrando na simulação de API no Storybook hoje, vamos adicionar uma história para nosso componente GitHubUser
que mostra seu comportamento padrão (bem-sucedido):
//histórias/GitHubUser.stories.js importar {GitHubUser} de'../src/GitHubUser' export default { título:'Usuário GitHub', componente: GitHubUser, } export const DefaultState=()=>
Saiba mais sobre escrita histórias na documentação do Storybook.
Nesse ponto, o componente seria renderizado, mas ainda faria uma solicitação HTTP real. É hora de adicionar um pouco de simulação de API à mistura.
Implementando simulação de API
Para permitir que o MSW saiba quais chamadas de API serão simuladas, precisamos declarar um conjunto de manipuladores de solicitação -funções que descrevem predicados de solicitação (quais solicitações capturar) e resolvedores de resposta (como responder a essas solicitações de). Posteriormente, os mesmos manipuladores de solicitação podem ser usados para declarar um trabalhador para simulação no navegador ou um “servidor” para simulação no ambiente Node.js.
Declarando manipuladores de solicitação
Crie um diretório src/mocks
em seu projeto para armazenar tudo relacionado à simulação de API. Nesse diretório, crie um arquivo chamado handlers.js
e declare o manipulador de solicitação para uma solicitação GET/user/: userId
seguindo este exemplo:
//src/mocks/handlers.js importar {rest} de'msw' exportar manipuladores const=[ //Capture uma solicitação GET/user/: userId, rest.get ('/user/: userId', (req, res, ctx)=> { //... e responda com esta resposta simulada. retornar res (ctx.json ({})) }), ]
Estamos declarando manipuladores de solicitação em um módulo separado porque eles podem ser reutilizados para vários propósitos: dentro de seu Storybook, durante o desenvolvimento local, para teste ou para depuração. Escreva uma vez e reutilize em qualquer lugar.
Ao escrever simulações, pense no MSW como um “servidor” simulado. Embora a biblioteca não estabeleça nenhum servidor real, ela atua como um servidor para seu aplicativo. Com isso em mente, eu recomendo manter os caminhos de “sucesso” de qualquer API no módulo mocks/handlers.js
global, enquanto delega as substituições por cenário (como respostas de erro) mais perto de cada indivíduo superfície de uso (ou seja, uma história específica ou um teste de integração).
O MSW usa um Service Worker para interceptar solicitações e simular respostas em um navegador. É por isso que vamos criar uma instância de worker
responsável por essa interceptação.
Use a API setupWorker
e forneça os manipuladores de solicitação declarados anteriormente para registrar e ativar o Service Worker que você inicializou durante a etapa de configuração.
//src/mocks/browser.js importar {setupWorker} de'msw' import {handlers} de'./handlers' export const worker=setupWorker (... handlers)
A interface do worker
expõe uma API para controle (como os métodos start
e stop
), mas não vamos trabalhar com isso ainda. Em vez disso, vamos delegar essa responsabilidade ao Storybook na próxima etapa.
MSW e integração de API
É crucial que as ferramentas que usamos sejam resilientes às mudanças. Esse é um dos principais motivos para adotar o MSW: por ser independente do cliente de solicitação, ele permite que você use a mesma integração, mesmo que seu aplicativo migre para uma biblioteca de solicitação diferente amanhã ou para uma convenção de API diferente.
Agora, vamos ativar a simulação de API globalmente no Storybook editando o arquivo .storybook/preview.js
para exigir condicionalmente o trabalhador e iniciá-lo:
//.storybook/preview.js if (typeof global.process==='undefined') { const {trabalhador}=requer ('../src/mocks/navegador') worker.start () }
A verificação
global.process
garante que o Storybook não tente ativar o Service Worker em um ambiente sem navegador, já quepreview.js
também é executado durante a construção do Storybook que é executado em Node.js.
Com esta etapa concluída, você pode ver a mensagem de ativação bem-sucedida do MSW no navegador DevTools em sua história:
Você pode ver que nossa solicitação foi tratada com êxito pelo MSW na IU e no Console do DevTools. A melhor parte dessa configuração é que não precisamos alterar nenhum código de nosso aplicativo! Ele ainda se comunica com a API do GitHub, mas recebe a resposta simulada que especificamos.
Os manipuladores de solicitação globais listados em src/mocks/handlers.js
são ótimos para manter as interações de API bem-sucedidas. No entanto, nem todas as interações são bem-sucedidas.
Se você deseja construir uma IU à prova de balas, você deve esperar erros e certificar-se de que seu componente pode lidar com eles sem problemas para um usuário. Além disso, você deve ser capaz de navegar pelas ilustrações visuais de seu componente em vários estados dependentes de rede nas respectivas histórias.
Respostas da API por história
Um dos benefícios do Storybook é a capacidade de exibir um único componente em vários estados. No caso de nosso componente, podemos ilustrar a manipulação de vários cenários de comunicação HTTP: o estado de carregamento enquanto nosso componente aguarda a resposta e uma resposta de erro da API GitHub. Para isso, você pode substituir os manipuladores de solicitação por história.
Usaremos decoradores de histórias para aprimorar uma história individual com gerenciadores de solicitação em tempo de execução -uma API para acrescentar ou reescrever manipuladores durante o tempo de execução quando a história é renderizada.
Simulando um estado de carregamento
As ações assíncronas podem demorar, e as chamadas HTTP não são uma exceção. Para garantir uma excelente experiência do usuário, nosso componente deve ser capaz de lidar com o estado de carregamento, enquanto nosso Storybook deve ilustrar esse estado de carregamento de uma maneira reproduzível e previsível.
Felizmente, você é o responsável pelas respostas simuladas, incluindo o tempo de resposta. Você não gostaria, no entanto, de afetar histórias não relacionadas, portanto, zombar de um estado de carregamento nos manipuladores de solicitação globais não é a melhor opção. Em vez disso, mantenha a lógica de simulação para o estado de carregamento ao lado da própria história. Veja como você pode fazer isso:
//src/stories/Component.story.js importar {rest} de'msw' importar {trabalhador} de'../mocks/browser' //Crie uma nova história de estado de carregamento. const LoadingState=()=>//Use decoradores de Storybook e manipuladores de tempo de execução MSW //para lidar com a mesma chamada HTTP de maneira diferente para esta história específica. LoadingState.decorators=[ (História)=> { worker.use ( rest.get ('https://api.github.com/users/:username', (req, res, ctx)=> { //Simule um estado de carregamento infinito. return res (ctx.delay ('infinito')) }) ) return }, ]
Observe como estamos usando um método worker.use ()
para provisionar um manipulador de solicitação em tempo de execução . Ainda fornecemos o mesmo método de solicitação e URL, mas uma função de resolução diferente que atrasa a resposta indefinidamente (consulte o utilitário ctx.delay
). Isso preserva a resposta em um estado pendente, que é exatamente o que você precisa para apresentar como seu componente lida com o estado de carregamento na IU.
Ao inspecionar a guia Rede nas DevTools do seu navegador, você pode ver que a solicitação da API do GitHub nunca é resolvida, o que nos permite visualizar exatamente esse estado em nossa história. É exatamente por isso que precisamos de simulação de API aqui-para ganhar flexibilidade e controle sobre as chamadas de API que nossos componentes fazem.
O MSW vem com uma API simples e uma variedade de utilitários para emular a resposta códigos de status , cabeçalhos , cookies do servidor e muitos outros para permitir a simulação de cenários do mundo real, como autenticação, CORS, ou streaming de conteúdo de mídia.
Simulação de respostas de erro
Semelhante ao estado de carregamento, você pode criar uma história separada para a resposta de erro e ter um manipulador de solicitação de tempo de execução que sempre responde com um erro de servidor HTTP específico.
//src/stories/Component.story.js importar {msw} de'msw' importar {trabalhador} de'../mocks/browser' const ErrorState=()=>ErrorState.decorators=[ (História)=> { worker.use ( rest.get ('https://api.github.com/users/:username', (req, res, ctx)=> { //Responda com um código de status de resposta 500. retornar res (ctx.status (500)) }) ) return }, ]
Use
ctx.status
e outros utilitários de contexto para modelar a resposta HTTP precisa que você precisa para mostre o comportamento do seu componente.
Salvando as alterações e navegando para o Storybook, testemunhamos um estado de erro reproduzível:
Embora nossa história agora mostre o tratamento de erros, clicar no botão Repetir ainda resulta em uma solicitação que sempre retorna uma resposta 500, assim como especificamos no gerenciador de solicitações em tempo de execução.
Seria ótimo retornar a resposta de erro apenas a primeira solicitação à API do GitHub é feita. Você pode fazer isso usando uma função res.once
em vez de res
em seu manipulador de tempo de execução:
rest.get ('https://api.github.com/users/:username', (req, res, ctx)=> { -retornar res (ctx.status (500)) + retorno de resposta (ctx.status (500)) })
Conclusão
Neste tutorial, aprendemos sobre a sinergia entre o Storybook e o Mock Service Worker, os benefícios do controle granular sobre as respostas da API simulada quando se trata de apresentar o mesmo componente em vários estados e como integrar as duas tecnologias juntas em de uma maneira perfeita.
Além disso, como o MSW pode ser executado no navegador e no Node.js, podemos reutilizar a mesma lógica de simulação de API para teste e desenvolvimento, concluindo uma integração frutífera e contínua.
Você pode encontrar o código-fonte deste exemplo em GitHub e saiba mais sobre a simulação de API na documentação MSW .
A postagem Usando o Storybook e Mock Service Worker para respostas de API simuladas apareceu primeiro no LogRocket Blog .