Testando componentes com Vue.js e Jest

Tecnologia

No primeiro artigo mostramos que existem diversas ferramentas para testar componentes no VueJS. Nesse artigo será mostrado como fazer isso com o framework jest.

Jest, segundo o site oficial, é um framework delicioso de se usar com foco na simplicidade. Nele vem tudo que precisamos – o test runner, pacote de assertions e um maduro sistema de criação de doubles. Apesar de estarmos utilizando em VueJS ele suporta os mais diversos frameworks.

Para este artigo utilizaremos a própria documentação do VueJS e dos framewoks utilizados em um simples projeto de uma agenda, que será disponibilizado no meu repositório do github.

O projeto base é feito com VueJS v2.6.11. Também é importante que você tenha conhecimento básico nas bibliotecas vue-router, vuex e vuetify, mas não são imprescindíveis. De fato, com um conhecimento em javascript deve ser possível acompanhar o artigo.

O projeto

Vamos trabalhar com um pequeno projeto que fiz especialmente para este artigo. Para continuar clone o projeto e instale as dependencias com os seguintes comandos

git clone https://github.com/rbentivenha/vuejs-jest.git
cd vuejs-jest
npm install

O projeto é apenas uma lista de nomes exibido em um card, utilizando dados de uma API que nos entregas dados de usuários de forma aleatória. Essa api é particularmente muito útil, pois nos retorna nomes, números de telefones, entre outros dados de forma aleatória para utilizarmos em nossas aplicações front-end. Vale a pena dar uma checada na documentação deles em https://randomuser.me/.

Sempre que inicio um projeto novo gosto de ler as documentações oficiais dos frameworks que utilizo antes de qualquer tutorial. Se você é assim como eu sugiro que visite os seguintes sites

A lista do projeto permite uma busca por nome. Assim, existem dois  componentes básicos, um campo de entrada de texto e o card. Os dados são armazenados em um store, utilizando a arquitetura do vuex. Também há uma camada de serviço para fazer conexão com a API.

Testando os componentes

Para testar os componentes é necessário montar e renderizá-los em um objeto. Nossa maravilhosa API do vue-test-util resolve esse problema com mount e shallowMount. Os dois fazem exatamente a mesma coisa, só que shallowMount usa doubles (componentes falsos) como filhos. Vamos ao teste do nosso exemplo, Card.vue

Powered by Rock Convert
import Vuetify from 'vuetify'
import Vue from 'vue'

import {
  mount,
  createLocalVue
} from '@vue/test-utils'

import Card from '../../src/components/Card'

const localVue = createLocalVue()
Vue.use(Vuetify)

Então, importamos o vuetify, vue, mount, createLocalVue e o  nosso componente. Como escolhemos usar o vuetify precisamos adicionar Vue.use(vuetify). Caso contrários os componentes padrões do vuetify (como VRow, entre outros) vão retornar algum erro de renderização. Portanto, fazemos isso para remover possíveis falsos positivos. Se você utilizou outro material design terá de fazer a mesma coisa com ele.

Nesse caso utilizamos o mount, porque não pretendemos usar stubs como filhos do nosso componente. O createLocalVue() é usado para adicionarmos plugins no nosso projeto de forma local com, como o Vue.use() e fica como opção para nosso uso. Vamos ao teste.

describe('Card.vue', () => {
  let vuetify

  beforeEach(() => {
    vuetify = new Vuetify()
  })

  it('renderiza card com exibindo as informações corretas', () => {
    const cardData = {
      localVue,
      vuetify,
      propsData: {
        name: 'Teste',
        phone: '(00)000000000',
        email: 'seu@email.com',
        end: 'RUA SEM NOME'
      }
    }
    const wrapper = mount(Card, cardData)
    Object.keys(cardData.propsData).forEach(key => {
      expect(wrapper.vm._props[key]).toMatch(cardData.propsData[key])
    })
  })
})

O teste é direto. Usamos o hook beforeEach para criar instâncias do vuetify para renderiziar de forma correta seus componentes e evitarmos falsos positivos. No teste criamos um objeto injetando o localVue, o vuetify e as props do componente em questão. Por fim fazemos a comparação se os dados colocados no props realmente são renderizados.

Rodando este teste com o comando npm run test vemos que o teste passa com sucesso.

É importante ressaltar que não usamos uma abordagem TDD, porque nesse caso teríamos que escrever o teste antes do código de forma que ele falharia na primeira vez que fosse executado.

E o store?

Ok, mas e se eu quiser testar o nosso store? Bom, o store é javascript puro. Assim, fica super simples testá-lo. Acompanhe o exemplo abaixo

import Contacts from '../../src/store/contacts'

describe('Contacts Store', () => {
  it('filter some payload', () => {
    const state = {
      contacts: [
        {
          name: {first: 'Teste'}
        }
      ]
    }
    Contacts.mutations.setSearchFilter(state, 'Teste')
    expect(state.filteredContent.length).toBe(0)
  })
})

Nesse caso o teste vai falhar. Eu uso a lógica do filtro para buscar o nome “Teste” no state e ele vai achar um registro, porque eu sei que ele existe no meu mock do state. Acontece que na assertion eu espero que ele seja 0 e ele será 1.

Podemos ver que ele realmente falhou, na saída temos qual teste falhou. Isso facilita muito a vida do programador, pois ele já vai ter ideia qual comportamento esperado do software não está de acordo com o esperado. No final o jest devolve o controle para o node que retorna o status 1 para o sistema operacional. Isso é particularmente útil em um continuous integration, pois em caso de falha de teste ele já pararia o build.

Conclusão

Em relação a testes de componentes no VueJS com Jest não há muitos segredos. A complexidade dos seus testes na verdade dependerá do seu código. Isso leva a outros aspecto interessante de implementar testes nos componentes, pois isso forçará que o desenvolvedor pense de forma mais concisa para que o este seja testável. Além disso seu componente estará documentado e qualquer dúvida que outro programador vier a ter ele pode resolver lendo o teste, o que é muito mais rápido do que tentar adivinhar como as coisas funcionam.