Fazendo uma aplicação em Vue.js com TDD — Um guia extensivo para quem quer aprender — parte 3

Testando a store e finalizando os nossos componentes

Daniel Kuroski
magnetis backstage
Published in
8 min readOct 26, 2018

--

Este é o terceiro de uma série de artigos:

Semana passada fizemos testes mais simples relacionados ao componente UserView. O mais interessante foi o refactor feito, criando uma base simples que será utilizada para testar qualquer componente.

Vamos agora nos aprofundar, e finalizar todos os testes dos nossos componentes, juto da integração com a store.

Integrando a store nos nossos testes

Primeiro, vamos alterar o nosso arquivo de state: src/store/state.js

export default {
user: {},
}

Vamos criar um arquivo de fixture, no qual ele irá conter um exemplo de como seria o nosso dado real para utilizarmos nos nossos testes.

tests/unit/fixtures/user.js
tests/unit/UserView.spec.js

O refactor é um pouco grande, mas vamos com calma para entender o que foi feito aqui.

Com o shallowMount, renderizamos o componente sem nenhuma dependência de terceiros e sem nenhuma configuração a mais.

Então antes do refactor, o componente não tinha como ter o contexto do Vuex ou da store ou de qualquer dependência que normalmente é instalada na inicialização, normalmente feita no main.js

Graças ao vue-test-utils, temos como criar uma instância local do vue para ser passada ao nosso componente, indicando para ele todas as dependências que que serão utilizadas.

  • Na linha 9, estamos criando esta instância local, fazendo exatamente o que é feito no main.js durante a instalação do Vuex
  • Na linha 13, criamos uma variavel de state, que ela poderá ser modificada entre os testes
  • Na linha 17 e 18, passamos para o nosso componente a nossa instância local do vue, assim como uma nova store (novamente, isso é exatamente o que está sendo feito no main.js )
  • Na linha 28, colocamos o beforeEach já que ele sempre é chamado entre cada teste (cada it ), aqui eu aproveito para “resetar” as variáveis utilizadas nos testes para o valor padrão, neste caso estamos fazendo uso do nosso arquivo original de state, e armazenando uma cópia dele entre cada teste
  • Na linha 37, finalmente podemos começar a ver um dos benefícios de utilizar a função de build. Garantimos que antes de executar esse teste, teremos uma variável de state resetada para o seu valor original, então podemos alterar o seu valor com o nosso fixture, contendo os dados “reais” do nosso usuário pesquisado, e SÓ AÍ que iríamos fazer o build do nosso componente.

Esta forma nos dá muita flexibilidade, pois podemos controlar os nossos testes, e fazer alterações antes de contruír o componente principal a ser testado.

E finalmente, podemos remover o nosso wrapper do teste, já que na linha 41, queremos garantir que o nosso componente está passando o usuário presente no state e não mais na data.

RED

Agora o teste está falhando, pois no UserView.vue não estamos passando o user da store para o VUserProfile.vue, vamos arrumar isso.

src/views/UserView.vue

Agora sim, inserimos o mapState mapeamos a propriedade user da store e passamos ela para o VUserProfile.

GREEN

Sucesso! Agora estamos garantindo que a UserView está passando a propriedade correta para o VUserProfile.

Último teste do UserView

Já estamos com o nosso componente quase coberto, falta apenas uma funcionalidade.

Eu gostaria de garantir sempre que o VUserSearchForm disparasse um evento submitted que fosse chamada uma action da store contendo o usuário que foi digitado nele.

tests/unit/UserView.spec.js

Vamos primeiro focar no teste:

  • Primeiro criamos uma variável contendo o usuário esperado a ser enviado pelo evento de submitted
  • Em seguida, emitimos manualmente um evento customisado do componente VUserSearchForm, que é justamente o evento que ele irá fazer futuramente
  • Nas linhas 50 e 51, nós esperamos que a action da store chamada SEARCH_USER tenha sido invocada, e que tenha sido passado como payload um objeto contendo o nosso usuário
  • A linha 51 pode parecer estranha, mas esta é a forma que temos de garantir que estamos passando o payload correto para actions da store, pois não estamos chamando manualmente o método da store

Quando um store.dispatch é executado, o Vuex internamente chama a nossa action por nós, e ele acaba injetando no primeiro parâmetro um objeto contendo dados que podemos utilizar na action, e como segundo parâmetro, é o nosso payload.

Então precisamos manualmente pegar o segundo parâmetro da chamada da store que é o payload (o nosso username).

Agora podemos entender como foi feita a preparação para este teste.

Na linha 1, estamos utilizando uma funcionalidade muito legal do jest, basicamente utilizando o jest.mock, o jest irá procurar por um src/store/__mocks__/actions.js ao invés do original src/store/actions.js

Isso permite com que a gente crie os nossos mocks, para que eles possam ser utilizados pela aplicação inteira.

Isso funciona também para dependências de terceiros, ele basicamente irá mockar todas as funções da dependência para você.

Então na linha 8, importamos as nossas actions da store, que na realidade é o arquivo de mock que iremos criar.

Inserimos as actions dentro da store na linha 22.

E finalmente na linha 34, estamos resetando entre cada teste, todas as mock functions para o estado original, para nenhum teste impactar nos resultados do outro.

RED

Nosso teste está falhando, primeiro precisamos garantir que estamos trabalhando com uma mock function.

src/store/__mocks__/actions.js

Aqui basicamente é retornado um objeto com a nossa função de SEARCH_USER com um valor “padrão”, que seria uma promise resolvida com o nosso usuário da fixture.

src/views/UserView.vue

Finalmente, no nosso UserView podemos ouvir os eventos de submitted enviados pelo VUserSearchForm, que ao ser chamado, podemos fazer o dispatch da nossa action.

GREEN!

Basicamente com estes testes, estamos cobrindo o nosso componente UserView 😄

Para os próximos componentes, que são apenas de apresentação, vamos acelerar um pouco, já que praticamente vamos testar as mesmas coisas.

Vou postar direto os arquivos completos de teste e de produção para poupar deste artigo ser AINDA maior, mas deveríamos seguir os mesmos passo-a-passo como foi feito anteriormente.

Testando o VUserProfile

tests/unit/VUserProfile.spec.js

Aqui não vemos nada de muito novo, como o VUserProfile é um componente apenas de apresentação.

src/components/VUserProfile.vue

Adicionamos aqui apenas a renderização dos dados recebidos via props.

GREEN

Para então ver tudo passar 😏

Testando o VUserSearchForm

tests/unit/VUserSearchForm.spec.js

Novamente, os testes aqui são muito semelhantes, apenas temos uma novidade, que é o último teste.

Ali, inserimos manualmente no input o nosso usuário pesquisado, e então fazemos o trigger dos eventos de input, indicando que “escrevemos” nele.

E finalmente, o clique/submit do botão, para então garantirmos que o evento foi emitido como esperado com o valor do input.

Com isso, vocês já aprenderam a base para testar componentes em Vue, é bem provável que não fuja muito disso, e não fique muito mais complicado que isso (há casos e casos 😅).

src/components/VUserSearchForm.vue

Para o código de produção, apenas renderizamos o formulário, e no submit emitimos um evento de submitted com o username como payload.

Testando a action

Agora cobrimos todos os nossos componentes de teste, podemos ir para a segunda parte, que seria testar a nossa store, as actions e mutations.

Existem duas formas de testar a store, e aqui eu vou trazer apenas uma delas, caso você queira descobrir mais, dê uma lida no livro do Testing Vue.js Applications.

Vamos começar testando a nossa action.

tests/unit/actions.spec.js

Neste nosso caso, temos apenas uma action, é feita uma chamada manual ao método SEARCH_USER com um commit mockado, enviando o nosso usuário como payload.

Na linha 20, temos o uso de uma dependência que baixamos lá no início do projeto, ela é só uma garantia, pois ao executar ela, garantimos que todas as promises foram resolvidas neste momento no nosso teste.

E finalmente, ao chamar a action, nós esperamos que o nosso serviço de API tenha sido chamado na função searchUser e que tenha sido passado o nosso usuário esperado.

Também, como estamos testando um caminho feliz, no qual a requisição feita foi feita com sucesso, nós esperamos que tenha sido chamado o commit com o nosso fixture, no qual representa um mock de resposta da API do Github.

src/__mocks__/api.js
// src/api.js
export default {}

Como estamos utilizando o jest.mock('@/api.js') precisamos criar o nosso arquivo de mock, mais tarde iremos criar o nosso serviço de requisição, mas por hora, para os nossos testes podemos ter o nosso arquivo de produção apenas com o export default.

Para não extender ainda mais o nosso artigo, eu vou apenas testar os caminhos felizes, não iremos cobrir aqui casos alternativos, como por exemplo, uma falha na requisição, network error, etc.

Bom… chegamos na fase:

RED

Basicamente o nosso método ainda não existe, podemos finalmente fazer a implementação de produção do nosso SEARCH_USER

src/store/actions.js

Podemos fazer o código de produção de diversas formas, nesse meu caso, eu optei por retornar uma promise.

E aqui, fazemos tudo o que estamos exigindo no nosso teste, primeiro eu estou chamando a nossa api.searchUser enviando o username que está sendo passado por parâmetro.

E logo em seguida que eu tenho a resposta, eu estou fazendo o commit de uma mutação enviando o nosso usuário retornado do serviço de API.

GREEN

Perfeito, todos os testes passando!

Testando a mutation

A mutation deve ser o caso mais simples que temos até agora.

tests/unit/mutations.spec.js

Aqui não tem nenhum segredo, antes de cada teste, resetamos o nosso “state” local, e o que estamos fazendo é, chamar diretamente a nossa mutation, mandando esse state, passando o nosso usuário.

No final, esperamos que o state tenha sido setado sendo o nosso usuário, e aqui eu quero garantir que é uma função pura, então quero que o valor do state sejauma cópia do usuário original.

RED

Agora que o nosso teste falhou, vamos para a implementação.

src/store/mutations.js

Pronto, eu sei que no caso do Vue.js eu não precisaria me preocupar em fazer uma cópia do objeto, pela forma como ele lida com reatividade, mas eu pessoalmente gosto de manter as minhas mutations como funções puras, não teria problema você fazer algo como: state.user = user , só não esqueça de mudar o teste caso você queira fazer desta forma.

GREEN

Nosso teste passou 😄

Resumo da obra

Resumindo

Neste terceiro artigo, fizemos:

  • Finalizamos todos os testes de componentes
  • Integramos os testes com a store

Fiquem atentos que próxima semana estaremos fazendo testes para o nosso serviço de requisições para a API.

Muito obrigado pela atenção, se gostou, por favor, clique no 💚, e qualquer dúvida, sugestões ou correções sinta-se a vontade para me mandar uma mensagem, eu agradeço muuito 😄.

Meu Twitter: @DKuroski

Vejo você próxima semana 😄

--

--