Como e Guia para Testes Unitários Componentes Vue

Este artigo serve como um guia para testes unitários de componentes Vue.

Veremos primeiro por que o teste de unidade é importante para a criação de software sustentável e o que você deve testar. Em seguida, detalharemos como:

  1. Criar e executar um teste de unidade para um componente Vue
  2. Testar diferentes aspectos de um componente Vue
  3. Use mocks para testar funções assíncronas
  4. Verifique a cobertura de código de seus testes de unidade
  5. Estruture seus arquivos de teste de unidade

Dependências :

O código-fonte (junto com instruções detalhadas de instalação) para o projeto Vue Weather App usado neste artigo pode ser encontrado no GitLab: Vue Weather App .

Objetivos

Ao final deste artigo, você deverá ser capaz de:

  1. Explique por que o teste de unidade é importante
  2. Descreva o que você deve (e não deve) testar a unidade
  3. Desenvolva um conjunto de testes de unidade para um componente Vue
  4. Execute os testes de unidade para um projeto Vue
  5. Utilize as funções beforeEach()e afterEach()dentro de um conjunto de testes de unidade
  6. Escreva testes de unidade para testar os detalhes de implementação de um componente Vue
  7. Escreva testes de unidade para testar os aspectos comportamentais de um componente Vue (eventos de clique, etc.)
  8. Explicar como a simulação pode ajudar a simplificar os testes de unidade
  9. Escreva testes de unidade para zombar de uma biblioteca e testar funções assíncronas
  10. Verifique a cobertura de código de seus testes de unidade
  11. Desenvolva um arquivo de teste de unidade bem estruturado para testar um componente Vue

Por que teste unitário?

Em geral, o teste ajuda a garantir que seu aplicativo funcione conforme o esperado para seus usuários finais.

Projetos de software com alta cobertura de teste nunca são perfeitos, mas é um bom indicador inicial da qualidade do software. Além disso, o código testável geralmente é um sinal de uma boa arquitetura de software, e é por isso que os desenvolvedores avançados levam os testes em consideração durante todo o ciclo de vida do desenvolvimento.

Os testes podem ser considerados em três níveis:

  1. Unidade
  2. Integração
  3. De ponta a ponta

Os testes de unidade testam a funcionalidade de uma unidade individual de código isolada de suas dependências. Eles são a primeira linha de defesa contra erros e inconsistências em sua base de código. O teste de unidade é uma parte fundamental do processo de Desenvolvimento Orientado a Testes (TDD).

O teste de unidade melhora a capacidade de manutenção do seu código.

Manutenibilidade refere-se a fazer correções de bugs ou aprimoramentos em seu código ou a outro desenvolvedor que precise atualizar seu código no futuro.

O teste de unidade deve ser combinado com um processo de Integração Contínua (CI) para garantir que seus testes de unidade sejam executados constantemente, idealmente em cada confirmação para seu repositório. Um conjunto sólido de testes de unidade pode ser fundamental para detectar defeitos rapidamente e no início do processo de desenvolvimento, antes que seus usuários finais os encontrem na produção.

O que testar

O que você deve testar? Ou, mais importante: O que você não deve testar?

Existem três tipos de testes a serem considerados com testes de unidade:

  1. Detalhes de implementação : a lógica de negócios subjacente que um componente usa para produzir um resultado com base em uma determinada entrada
  2. Interface pública/contrato de design : entradas específicas (ou adereços, neste caso) produzem resultados específicos
  3. Efeitos colaterais : "se isso, então aquilo"; por exemplo, quando um botão é clicado, algo acontece

Como você não pode testar todas as coisas, em que você deve se concentrar?

Concentre-se em testar entradas e saídas com as quais o usuário final irá interagir. A experiência que os usuários do seu produto têm é primordial!

  1. Entradas : dados, adereços, interação do usuário, métodos de ciclo de vida, armazenamento de dados ( Pinia / Vuex ), parâmetros de rota, strings de consulta
  2. Saídas : saída renderizada, eventos, resultados de dados, atualizações de armazenamento de dados (Pinia/Vuex), despachos

Ao se concentrar em testar as entradas/saídas de um módulo de software (por exemplo, um componente Vue), você está testando os principais aspectos que o usuário final experimentará.

Pode haver outra lógica interna em um módulo de software que seja complexa e precise ser testada na unidade, mas é mais provável que esses tipos de testes precisem de atualização durante uma refatoração de código.

Visão geral do aplicativo

Antes de discutir como testar a unidade dos componentes Vue, quero dar uma breve visão geral do aplicativo Vue Weather que testaremos.

Depois de clonar o repositório, instale as dependências e adicione a chave de API.

Revise o README do projeto para obter mais informações sobre como criar e adicionar a chave de API do Open Weather.

Uma vez feito, o aplicativo Vue Weather pode ser executado em seu computador local iniciando o servidor de desenvolvimento:

$ npm run dev

Depois que o aplicativo for criado, você verá uma mensagem de sucesso semelhante a:

vite v2.9.14 dev server running at:

 > Local: http://localhost:3000/
 > Network: use `--host` to expose

 ready in 543ms.

Neste ponto, o servidor de desenvolvimento estará funcionando. Você pode ver o aplicativo Vue navegando até http://localhost:3000 em seu navegador favorito. Quando você carrega o aplicativo pela primeira vez, nenhum dado é exibido; você verá apenas um campo de entrada para inserir a cidade para a qual deseja obter o clima:

Passo a passo do aplicativo Vue Weather - Etapa 1

Sem nenhum dado inserido, os botões 'Pesquisar' e 'Limpar' ficam desabilitados.

Assim que você começar a inserir dados para uma cidade (assim que o primeiro caractere for adicionado), os botões 'Pesquisar' e 'Limpar' serão ativados:

Passo a passo do aplicativo Vue Weather - Etapa 2

Se você clicar em 'Pesquisar' depois de inserir uma cidade válida, os dados meteorológicos serão exibidos para essa cidade:

Passo a passo do aplicativo Vue Weather - Etapa 3

Neste ponto, clicar no botão 'Clear Weather Data' fará com que os dados meteorológicos sejam apagados:

Passo a passo do aplicativo Vue Weather - Etapa 4

No entanto, a cidade que foi inserida permanecerá no campo de entrada.

Se você clicar em 'Limpar' agora, o campo de entrada será limpo e os botões 'Pesquisar' e 'Limpar' serão desativados novamente:

Passo a passo do aplicativo Vue Weather - Etapa 5

O que acontece se você inserir uma cidade inválida? E se você clicar em 'Limpar' antes de 'Limpar dados meteorológicos'? Quais outros estados do aplicativo você pode encontrar?

Teste de unidade em Vue

Como os componentes são os blocos de construção do Vue (e realmente de qualquer estrutura SPA ), eles são a parte mais crucial do seu aplicativo como um todo. Portanto, gaste a maior parte do seu tempo de teste alocado escrevendo testes de unidade que testam os componentes do seu aplicativo.

Com base na minha experiência com componentes Vue de teste de unidade, descobri que as ferramentas e a estrutura de teste - Vite e Vitest - satisfazem os principais aspectos de um bom ambiente de teste:

  • os testes são fáceis e divertidos de escrever
  • testes podem ser escritos rapidamente
  • testes podem ser executados com um único comando
  • os testes são executados rapidamente

Felizmente, você descobrirá que o teste de unidade de seus componentes Vue é uma experiência agradável, pois acho que isso é fundamental para incentivar mais testes.

Ferramentas de teste de unidade

Existem duas ferramentas que vamos usar para testes unitários dos componentes Vue:

  1. Vitest - estrutura de teste de unidade
  2. Vue Test Utils - biblioteca de utilitários de teste de unidade para Vue

Vitest é uma estrutura de teste de unidade que visa ser agnóstica de estrutura, para que possa ser usada para testar Vue, React, Svelte, Lit e outros projetos. O Vitest está configurado para ser executado com o Vite, o que resulta em uma execução de teste rápida .

Se você estiver familiarizado com o Jest, mudar para o Vitest é muito simples, pois a API do Vitest é compatível com o Jest.

Ao escrever testes de unidade para um componente Vue, Vitest e Vue Test Utils fornecem a seguinte funcionalidade:

engrenagem

  • Ferramenta de linha de comando para executar os testes e verificar a cobertura do teste
  • Interface do usuário para ver visualmente os resultados dos testes e os resultados da cobertura
  • Funções para escrever testes de unidade ( it, describe)
  • Funções para verificar os valores esperados ( expect, toMatch, toContain, etc.)
  • Zombando ( mockResolvedValue, mockRejectedValue)
  • Configuração ( beforeEach, beforeAll) / Desmontagem ( afterEach, afterAll)

Ver Utilitários de Teste

  • Componentes de montagem ( mount, shallowMount)
  • Configurando dados de adereços ( setProps)
  • Encontrando componentes HTML para teste ( findAll('h2'))
  • Utilitário para liberar todas as Promessas ( flushPromises())
  • Utilitário para acionar eventos de clique ( trigger)
  • Utilitário para verificar eventos emitidos ( emitted)

O Vitest fornece a funcionalidade genérica para escrever testes de unidade enquanto o Vue Test Utils fornece os utilitários de teste específicos do Vue.

Visão geral do teste de unidade

Para começar, vamos discutir a convenção de nomenclatura para testes de unidade no Vue. O arquivo de teste de unidade deve ter o seguinte formato:

  • <ComponentName>.spec.js

De acordo com o Guia de Estilo Vue , os nomes dos componentes devem ter várias palavras ( WeatherHeaderem vez de apenas Header) para evitar conflitos com elementos HTML.

Você normalmente deve ter um arquivo de teste de unidade para cada componente em seu projeto Vue. Dentro de cada arquivo de teste de unidade, pode haver um único conjunto de testes de unidade ou vários conjuntos de testes de unidade.

Os arquivos de teste de unidade devem ser colocados em uma subpasta dentro da pasta Componentes ( src/components/__tests__ ):

$ tree -d -L 2
.
├── node_modules
├── public
├── src
    ├── assets
    └── components
        └── __tests__

Executando os testes

O Vitest pode ser usado para executar os testes de unidade assim:

$ npm run test:unit

✓ src/components/__tests__/WeatherFooter.spec.js (1)
✓ src/components/__tests__/WeatherHeader.spec.js (1)
✓ src/components/__tests__/WeatherResult.spec.js (3)
✓ src/components/__tests__/WeatherBanner.spec.js (5)
✓ src/components/__tests__/WeatherSearch.spec.js (5)
✓ src/components/__tests__/App.spec.js (7)

Test Files  6 passed (6)
    Tests  22 passed (22)
     Time  2.38s (in thread 640ms, 371.13%)

Todos os comandos disponíveis para execução npmem seu projeto Vue são definidos no scriptscampo em package.json .

A configuração padrão do Vitest é executar os testes no modo de observação , o que significa que o conjunto de testes será executado novamente a cada salvamento em um arquivo aplicável. Para alterar essa configuração para que o Vitest seja executado apenas uma vez (sem "modo de observação"), atualize a test:unitconfiguração em package.json para incluir o runargumento:

"test:unit": "vitest run --environment jsdom",

Exemplos

Usando o aplicativo Vue Weather , vamos ver alguns exemplos para testar componentes Vue.

Exemplo 1 - Introdução ao Teste Unitário

Vamos pular direto para um exemplo de um arquivo de teste de unidade no Vue! O primeiro arquivo de teste de unidade é encontrado em src/components/__tests__/WeatherHeader.spec.js e testa o WeatherHeadercomponente:

import { describe, it, expect } from 'vitest'
import { shallowMount } from '@vue/test-utils'
import WeatherHeader from '../WeatherHeader.vue'


describe('WeatherHeader.vue Test', () => {
  it('renders message when component is created', () => {
    // render the component
    const wrapper = shallowMount(WeatherHeader, {
      propsData: {
        title: 'Vue Project'
      }
    })

    // check that the title is rendered
    expect(wrapper.text()).toMatch('Vue Project')
  })
})

Montagem

A primeira linha neste arquivo importa as funções de teste do Vitest que são usadas neste arquivo.

Se você quiser que a API Vitest esteja disponível globalmente (como o Jest funciona), adicione test: {globals: true}a defineConfig()função em vite.config.js . Para mais detalhes, confira a documentação do Vitest .

A segunda linha neste arquivo importa uma função chamada shallowMountda biblioteca Vue Test Utils. A ideia de 'montagem' significa o carregamento de um componente individual para poder testá-lo. Existem dois métodos para isso no Vue Test Utils:

  • shallowMount()- cria um wrapperpara o componente Vue, mas com componentes filhos stubbed
  • mount()- cria um wrapperpara o componente Vue, incluindo a montagem de qualquer componente filho

Como nosso foco é testar um componente individual (o WeatherHeadercomponente), usaremos shallowMount().

shallowMount()é melhor para testar um componente individual isoladamente, pois os componentes filhos são stubs. Esta é a situação ideal para testes unitários .

Além disso, usar shallowMount()para testar um componente com muitos componentes filho pode melhorar o tempo de execução do teste de unidade, pois não há custo (em termos de tempo) para renderizar os componentes filhos.

mount()é útil quando você deseja incluir o teste do comportamento dos componentes filho.

A terceira linha importa o componente Vue que será testado, WeatherHeader.vue .

Descrever Blocos

Após as importinstruções, há um describebloco que define um conjunto de testes de unidade.

Dentro de um arquivo de teste de unidade, pode haver vários describeblocos que definem diferentes conjuntos de testes de unidade. Da mesma forma, cada describebloco pode conter vários testes de unidade, onde cada teste de unidade é definido por um itbloco.

Considero essa distinção como:

  • describebloco - suíte de teste de unidade
  • itbloco - função de teste de unidade individual

O que é bom sobre o teste de unidade com o Vitest é que existem vários incentivos embutidos para adicionar comentários. Por exemplo, o primeiro argumento para describedeve explicar claramente qual componente Vue está sendo testado:

describe('WeatherHeader.vue Test', () => { ... })

Para cada itbloco, o primeiro argumento é uma descrição da função de teste, que deve ser uma breve descrição do que esse teste específico está fazendo. No exemplo acima, o itbloco testa se o componente 'renderiza a mensagem quando o componente é criado'.

Espera

Quanto ao teste unitário real, o primeiro passo é montar o componente Vue para que possa ser testado:

// render the component
const wrapper = shallowMount(WeatherHeader, {
  propsData: {
    title: 'Vue Project'
  }
})

A shallowMountfunção retorna um wrapperobjeto, que contém o componente montado e os métodos para testar esse componente. O wrapperobjeto nos permite testar todos os aspectos do HTML gerado pelo componente Vue e todas as propriedades (como dados) do componente Vue.

Além disso, as props, passadas para o WeatherHeadercomponente, são passadas como o segundo argumento para shallowMount().

A verificação real realizada no teste de unidade é:

// check that the title is rendered
expect(wrapper.text()).toMatch('Vue Project')

Esta linha serve wrapperpara verificar se o título gerado pelo componente é 'Vue Project'. Como essa verificação faz uma comparação de strings, é recomendável usar toMatch().

Auxiliares de teste

Enquanto as verificações no arquivo de teste de unidade para o WeatherHeadercomponente estão apenas verificando valores de string, existem [muitas opções disponíveis no Vitest para realizar verificações:

  • Booleanos:
    • toBeTruthy()- verifica se uma variável/instrução é verdadeira
    • toBeFalsy()- verifica se uma variável/instrução é falsa
  • Definiram:
    • toBeNull()- verifica se uma variável corresponde apenas a null
    • toBeUndefined()- verifica se uma variável não está definida
    • toBeDefined()- verifica se uma variável está definida
  • Números:
    • toBeGreaterThan()- verifica se um número é maior que o valor especificado
    • toBeGreaterThanOrEqual()- verifica se um número é maior ou igual ao valor especificado
    • toBeLessThan()- verifica se um número é menor que o valor especificado
    • toBeLessThanOrEqual()- verifica se um número é menor ou igual ao valor especificado
    • toBe()e toEqual()- verifica se um número é igual ao valor especificado (essas funções são equivalentes para números)
    • toBeCloseTo()- verifica se um número é igual ao valor especificado dentro de uma pequena tolerância (útil para números de ponto flutuante)
  • Cordas:
    • toMatch()- verifica se uma string é igual ao valor especificado (Regex pode ser usado como o valor especificado!)
  • Matrizes:
    • toContain()- verifica se um array contém o valor especificado

Além disso, o notqualificador pode ser usado com a maioria destas verificações:

expect(wrapper.text()).not.toMatch('Node Project')

Para obter uma lista completa de verificações disponíveis, confira a referência da API Vitest .

Exemplo 2 - Testando as Condições Iniciais

Este exemplo mostra como testar as condições iniciais (ou estado) do WeatherResultcomponente.

Aqui está um esboço do arquivo de teste de unidade (definido em src/components/__tests__/WeatherResult.spec.js ):

import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { shallowMount, flushPromises } from '@vue/test-utils'
import WeatherResult from '@/components/WeatherResult.vue'


describe('WeatherResult.vue Implementation Test', () => {
  let wrapper = null

  // SETUP - run before to each unit test
  beforeEach(() => {
    // render the component
    wrapper = shallowMount(WeatherResult, {
      propsData: {
        city: '',
        weatherSummary: '',
        weatherDescription: '',
        currentTemperature: 0.0,
        lowTemperature: 0.0,
        highTemperature: 0.0
      }
    })
  })

  // TEARDOWN - run after to each unit test
  afterEach(() => {
    wrapper.unmount()
  })

  it('initializes with correct elements', () => { ... })

  it('processes valid props data', async () => { ... })

  it('emits a custom event when the Clear Weather Data button is clicked', () => { ... })
})

O arquivo de teste de unidade utiliza a shallowMount()função para renderizar o WeatherResultcomponente, pois esse componente é testado como um componente individual isoladamente.

blocos antes de cada e depois de cada

Dentro do conjunto de testes de unidade (definido dentro do describebloco), existem duas novas funções definidas:

  • beforeEach()- chamado antes da execução de cada teste de unidade dentro deste conjunto de testes de unidade
  • afterEach()- chamado após a execução de cada teste de unidade dentro deste conjunto de testes de unidade

A beforeEach()função é usada para definir um estado consistente antes de executar cada teste de unidade. Esse conceito é muito importante para garantir que a ordem de execução dos testes unitários não afete os resultados dos testes unitários como um todo.

Neste exemplo, a beforeEach()função renderiza o componente com um conjunto padrão de dados de prop:

// SETUP - run before to each unit test
beforeEach(() => {
  // render the component
  wrapper = shallowMount(WeatherResult, {
    propsData: {
      city: '',
      weatherSummary: '',
      weatherDescription: '',
      currentTemperature: 0.0,
      lowTemperature: 0.0,
      highTemperature: 0.0
    }
  })
})

A afterEach()função é usada para limpar qualquer processamento realizado durante o teste de unidade.

Neste exemplo, a afterEach()função desmonta o wrapperusado durante o teste de unidade, para que wrapperpossa ser reinicializado para o próximo teste de unidade no beforeEach():

// TEARDOWN - run after to each unit test
afterEach(() => {
  wrapper.unmount()
})

Se você deseja executar o código que é executado antes ou depois da execução do conjunto geral de testes de unidade, você pode usar:

beforeAll(() => {
  /* Runs before all tests */
})
afterAll(() => {
  /* Runs after all tests */
})

Espera

O primeiro teste de unidade verifica as condições iniciais do WeatherResultcomponente:

it('initializes with correct elements', () => {
  // check that the heading text is rendered
  expect(wrapper.findAll('h2').length).toEqual(2)
  expect(wrapper.findAll('h2').at(0).text()).toMatch('Weather Summary')
  expect(wrapper.findAll('h2').at(1).text()).toMatch('Temperatures')

  // check that 6 fields of data for the temperature are displayed
  expect(wrapper.findAll('p').length).toEqual(6)
  expect(wrapper.findAll('p').at(0).text()).toMatch('City:')
  expect(wrapper.findAll('p').at(1).text()).toMatch('Summary:')
  expect(wrapper.findAll('p').at(2).text()).toMatch('Details:')
  expect(wrapper.findAll('p').at(3).text()).toMatch('Current: 0° F')
  expect(wrapper.findAll('p').at(4).text()).toMatch('High (Today): 0° F')
  expect(wrapper.findAll('p').at(5).text()).toMatch('Low (Today): 0° F')
})

Verificações:

  1. A primeira seção de expects verifica se os dois cabeçalhos (definidos como h2elementos) estão conforme o esperado.
  2. A segunda seção de expects verifica se os seis campos de dados (definidos como pelementos) estão conforme o esperado.

Exemplo 3 - Adereços de Teste

O segundo teste de unidade verifica se os dados válidos passados ​​como dados prop são tratados corretamente pelo WeatherResultcomponente:

it('processes valid props data', async () => {
  // Update the props passed in to the WeatherResult component
  wrapper.setProps({
    city: 'Chicago',
    weatherSummary: 'Cloudy',
    weatherDescription: 'Cloudy with a chance of rain',
    currentTemperature: 45.1,
    lowTemperature: 42.0,
    highTemperature: 47.7
  })

  // Wait until the DOM updates
  await flushPromises()

  // check that the prop data is stored as expected within the component
  expect(wrapper.vm.city).toMatch('Chicago')
  expect(wrapper.vm.weatherSummary).toMatch('Cloudy')
  expect(wrapper.vm.weatherDescription).toMatch('Cloudy with a chance of rain')
  expect(wrapper.vm.currentTemperature).toEqual(45.1)
  expect(wrapper.vm.lowTemperature).toBeCloseTo(42.0)
  expect(wrapper.vm.highTemperature).toBe(47.7)

  // check that the heading text is rendered
  expect(wrapper.findAll('h2').length).toEqual(2)
  expect(wrapper.findAll('h2').at(0).text()).toMatch('Weather Summary')
  expect(wrapper.findAll('h2').at(1).text()).toMatch('Temperatures')

  // check that 6 fields of data for the temperature are displayed
  expect(wrapper.findAll('p').length).toEqual(6)
  expect(wrapper.findAll('p').at(0).text()).toMatch('City: Chicago')
  expect(wrapper.findAll('p').at(1).text()).toMatch('Summary: Cloudy')
  expect(wrapper.findAll('p').at(2).text()).toMatch('Details: Cloudy with a chance of rain')
  expect(wrapper.findAll('p').at(3).text()).toMatch('Current: 45.1° F')
  expect(wrapper.findAll('p').at(4).text()).toMatch('High (Today): 47.7° F')
  expect(wrapper.findAll('p').at(5).text()).toMatch('Low (Today): 42° F')
})

Como a beforeEach()função fornece um conjunto padrão de dados de prop, precisamos substituir os dados de prop usando a setProps()função.

Para garantir que os dados do prop causem as atualizações esperadas no WeatherResult, o teste precisa aguardar que todas as atualizações do DOM entrem em vigor:

// Wait until the DOM updates
await flushPromises()

NOTA: O uso de awaitsó é possível se a função for definida com async!

Verificações:

  1. Com os dados de props atualizados, podemos verificar se os dados de props foram armazenados corretamente no WeatherResultcomponente verificando os elementos de dados (usando wrapper.vm).
  2. A segunda seção de expects verifica se os dois cabeçalhos (definidos como h2elementos) estão conforme o esperado.
  3. O último conjunto de expects verifica se os dados da prop são usados ​​para definir os seis campos de dados (definidos como pelementos) conforme o esperado.

Exemplo 4 - Testando a entrada do usuário (Evento de clique)

O terceiro teste de unidade verifica se o clear-weather-dataevento é emitido pelo WeatherResultcomponente quando o usuário clica no botão 'Clear Weather Data':

it('emits a custom event when the Clear Weather Data button is clicked', () => {
  // trigger an event when the 'Clear Weather Data' button is clicked
  wrapper.findAll('button').at(0).trigger('click')

  // check that 1 occurrence of the event has been emitted
  expect(wrapper.emitted('clear-weather-data')).toBeTruthy()
  expect(wrapper.emitted('clear-weather-data').length).toBe(1)
})

Para acionar um evento de clique, o buttonelemento deve ser encontrado no wrappere então a triggerfunção é chamada para acionar o evento de clique.

Uma vez que o botão é clicado, o teste de unidade verifica se apenas um evento personalizado (com o nome de clear-weather-data) é emitido.

Exemplos de zombaria

Dentro do Appcomponente, quando um usuário pesquisa o clima de uma cidade, uma chamada HTTP GET é feita para o Open Weather para recuperar os dados por meio de uma biblioteca de terceiros chamada Axios :

const searchCity = (inputCity) => {
  // GET request for user data
  axios.get('http://api.openweathermap.org/data/2.5/weather?q=' + inputCity + '&units=imperial&APPID=' + openweathermapApiKey.value)
    .then((response) => {
      // handle success
      console.log(response)

      weatherData.value.city = response.data.name
      weatherData.value.weatherSummary = response.data.weather[0].main
      weatherData.value.weatherDescription = response.data.weather[0].description
      weatherData.value.currentTemperature = response.data.main.temp
      weatherData.value.lowTemperature = response.data.main.temp_min
      weatherData.value.highTemperature = response.data.main.temp_max
      validWeatherData.value = true
    })
    .catch((error) => {
      // handle error
      messageType.value = 'Error'
      messageToDisplay.value = 'ERROR! Unable to retrieve weather data for ' + inputCity + '!'
      console.log(error.message)
      resetData()
    })
    .finally((response) => {
      // always executed
      console.log('HTTP GET Finished!')
    })
}

Ao pensar em como testar as chamadas HTTP GET, dois cenários vêm à mente, cada um testando o efeito colateral da chamada real da API:

  • Resposta HTTP GET bem-sucedida ( caminho feliz )
  • Falha na resposta HTTP GET ( caminho de exceção )

Ao testar o código que utiliza uma API externa, geralmente é mais fácil não fazer a chamada real, substituindo a chamada por uma simulação. Existem prós e contras nessa abordagem, no entanto.

Prós:

  1. Os testes não dependerão de uma solicitação de rede
  2. Eles não vão quebrar se a API cair
  3. Eles vão correr muito mais rápido

Contras:

  1. Você precisará atualizar os testes sempre que o esquema da API for alterado
  2. É difícil acompanhar as mudanças de API em uma arquitetura de microsserviço
  3. A simulação é um conceito difícil de entender e pode adicionar muita confusão aos seus conjuntos de testes

Em algum momento, você deve verificar a integração completa para garantir que a forma da resposta da API não foi alterada.

Como este artigo é focado em testes de unidade, vamos zombar da biblioteca Axios.

A simulação fornece um meio de imitar o comportamento esperado de um módulo de software. Embora o mocking possa ser usado no código de produção (muito perigoso!), normalmente é usado durante o desenvolvimento e teste.

O carregamento de dados de uma API externa leva tempo. Embora normalmente leve menos de um ou dois segundos para carregar os dados do Open Weather neste aplicativo, outras APIs externas podem consumir mais tempo. Além disso, queremos uma maneira de verificar facilmente se a solicitação HTTP GET falha. Portanto, adicionaremos simulações para especificar como a solicitação GET responderia.

Exemplo 5 - Teste de código assíncrono (caso de sucesso)

Os testes de unidade para o Appcomponente estão localizados no arquivo src/components/__tests__/App.spec.js .

Para começar, precisamos importda biblioteca Axios:

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { shallowMount, mount, flushPromises } from '@vue/test-utils'
import App from '../../App.vue'
import axios from 'axios'

No entanto, não queremos usar a biblioteca Axios real como é feito no código-fonte ( App.vue ). Em vez disso, queremos criar uma simulação da biblioteca Axios para que não chamemos a API externa:

// Mock the axios library
vi.mock("axios", () => {
  return {
    default: {
      get: vi.fn(),
    },
  };
});

Este bloco de código diz ao Vitest que o get()método dentro da axiosbiblioteca deve ser simulado.

No arquivo de teste de unidade ( src/components/__tests__/App.spec.js ), para o Appcomponente, temos:

  1. Simulando uma solicitação HTTP GET bem-sucedida
  2. Simulando uma solicitação HTTP GET com falha

Para começar, vamos lidar com a situação nominal em que a solicitação HTTP GET é bem-sucedida.

describe('Implementation Test for App.vue with Successful HTTP GET', () => {
  let wrapper = null

  beforeEach(() => {
    const responseGet = { data:
      {
        name: 'Chicago',
        weather: [
          {
            main: 'Cloudy',
            description: 'Cloudy with a chance of rain'
          }
        ],
        main: {
          temp: 56.3,
          temp_min: 53.8,
          temp_max: 58.6
        }
      }
    }

    // Set the mock call to GET to return a successful GET response
    axios.get.mockResolvedValue(responseGet)

    // render the component
    wrapper = shallowMount(App)
  })

  afterEach(() => {
    axios.get.mockReset()
    wrapper.unmount()
  })

  ...

})

blocos antes de cada e depois de cada

Na beforeEach()função, definimos a resposta que deve ocorrer quando axios.get()for chamado. A resposta são os dados meteorológicos pré-enlatados que se parecem com o que obtemos do Open Weather, se realmente fizermos a solicitação. A linha chave nesta seção é:

// Set the mock call to GET to return a successful GET response
axios.get.mockResolvedValue(responseGet)

Esta linha é a chave, pois permite ao Vitest saber que deve retornar o responseGetarray quando axios.get()for chamado no Appcomponente, em vez de realmente fazer uma chamada HTTP GET usando o Axios.

A próxima seção do conjunto de testes de unidade define a afterEach()função:

afterEach(() => {
  axios.get.mockReset()
  wrapper.unmount()
})

A afterEach()função, que é chamada após a execução de cada teste de unidade, limpa a simulação axios.get()que foi criada durante a execução do teste de unidade. Essa abordagem é uma boa prática para limpar quaisquer simulações após a execução de um teste, para que todos os testes subsequentes comecem a partir de um estado conhecido.

Espera

Agora podemos definir o teste de unidade:

it('does load the weather data when a successful HTTP GET occurs', async () => {
  wrapper.vm.searchCity('Chicago')

  // Wait until the DOM updates
  await flushPromises()

  expect(axios.get).toHaveBeenCalledTimes(1)
  expect(axios.get).toBeCalledWith(expect.stringMatching(/Chicago/))

  // check that the user data is properly set
  expect(wrapper.vm.weatherData.city).toMatch('Chicago')
  expect(wrapper.vm.weatherData.weatherSummary).toMatch('Cloudy')
  expect(wrapper.vm.weatherData.weatherDescription).toMatch('Cloudy with a chance of rain')
  expect(wrapper.vm.weatherData.currentTemperature).toEqual(56.3)
  expect(wrapper.vm.weatherData.lowTemperature).toEqual(53.8)
  expect(wrapper.vm.weatherData.highTemperature).toEqual(58.6)
  expect(wrapper.vm.validWeatherData).toBe(true)
})

Como já passamos pelas etapas de definição do mock e renderização do componente (via shallowMount()), este teste de unidade pode se concentrar em realizar verificações.

O teste de unidade começa chamando a searchCity()função:

wrapper.vm.searchCity('Chicago')

Para garantir que a searchCity()função cause as atualizações esperadas no Appcomponente, o teste precisa aguardar que todas as Promises sejam resolvidas e que as atualizações do DOM entrem em vigor:

// Wait until all Promises are resolved and the DOM updates
await flushPromises()

Verificamos que axios.get()foi chamado apenas uma vez e que a chamada HTTP GET incluiu o nome correto da cidade:

expect(axios.get).toHaveBeenCalledTimes(1)
expect(axios.get).toBeCalledWith(expect.stringMatching(/Chicago/))

Para ser muito completo, os dados meteorológicos também verificam a instância do Appcomponente renderizado neste teste de unidade para garantir que ele corresponda aos dados pré-enlatados retornados da simulação de axios.get():

// check that the user data is properly set
expect(wrapper.vm.weatherData.city).toMatch('Chicago')
expect(wrapper.vm.weatherData.weatherSummary).toMatch('Cloudy')
expect(wrapper.vm.weatherData.weatherDescription).toMatch('Cloudy with a chance of rain')
expect(wrapper.vm.weatherData.currentTemperature).toEqual(56.3)
expect(wrapper.vm.weatherData.lowTemperature).toEqual(53.8)
expect(wrapper.vm.weatherData.highTemperature).toEqual(58.6)
expect(wrapper.vm.validWeatherData).toBe(true)

Exemplo 6 - Teste de código assíncrono (caso de falha)

Embora seja bom testar quando as coisas acontecem conforme o esperado, também é importante verificar como nosso software reage a condições negativas. Com isso em mente, vamos criar um segundo conjunto de testes de unidade para verificar uma solicitação HTTP GET com falha:

describe('Implementation Test for App.vue with Failed HTTP GET', () => { ... })

Não há problema em ter vários conjuntos de testes de unidade em um único arquivo de teste de unidade ( .spec.js ).

Como os mocks são criados na beforeEach()função, é necessário haver conjuntos de testes de unidade separados com beforeEach()implementações diferentes para respostas HTTP GET bem-sucedidas e com falha.

blocos antes de cada e depois de cada

A beforeEach()função neste conjunto de testes de unidade é bem diferente:

beforeEach(() => {
  // Set the mock call to GET to return a failed GET request
  axios.get.mockRejectedValue(new Error('BAD REQUEST'))

  // Render the component
  wrapper = shallowMount(App)
})

Em vez de definir uma resposta para retornar da axios.get()chamada, agora estamos retornando um Promiseobjeto com falha com a resposta de 'BAD REQUEST'.

A afterEach()função para este conjunto de testes de unidade é idêntica ao outro conjunto de testes de unidade:

afterEach(() => {
  axios.get.mockReset()
  wrapper.unmount()
})

Espera

Aqui está a função de teste de unidade para testar uma solicitação HTTP GET com falha:

it('does not load the weather data when a failed HTTP GET occurs', async () => {
  wrapper.vm.searchCity('Chicago')

  expect(axios.get).toHaveBeenCalledTimes(1)
  expect(axios.get).toBeCalledWith(expect.stringMatching(/Chicago/))

  // Wait until the DOM updates
  await flushPromises()

  // Check that there is no user data loaded when the GET request fails
  expect(wrapper.vm.weatherData.city).toMatch(/^$/)
  expect(wrapper.vm.weatherData.weatherSummary).toMatch(/^$/)
  expect(wrapper.vm.weatherData.weatherDescription).toMatch(/^$/)
  expect(wrapper.vm.weatherData.currentTemperature).toEqual(0)
  expect(wrapper.vm.weatherData.lowTemperature).toEqual(0)
  expect(wrapper.vm.weatherData.highTemperature).toEqual(0)
  expect(wrapper.vm.validWeatherData).toBe(false)

  // check that the banner message indicates failure
  expect(wrapper.vm.messageToDisplay).toMatch('ERROR! Unable to retrieve weather data for Chicago!')
  expect(wrapper.vm.messageType).toMatch('Error')
})

Assim como no conjunto de testes de unidade anterior, verificamos se apenas uma instância de axios.get()é chamada e, em seguida, verificamos se nenhum dado climático foi carregado no Appcomponente.

Cobertura de código

Ao desenvolver testes de unidade, pode ser bom entender quanto do código-fonte é realmente testado. Esse conceito é conhecido como cobertura de código.

Preciso deixar bem claro que ter um conjunto de testes de unidade que cobrem 100% do código-fonte não é de forma alguma um indicador de que o código foi testado adequadamente.

Essa métrica significa que há muitos testes de unidade e muito esforço foi feito para desenvolver os testes de unidade. A qualidade dos testes unitários ainda precisa ser verificada por inspeção de código.

O outro extremo onde este é um conjunto mínimo (ou nenhum!) de testes de unidade é um indicador muito ruim.

Vitest fornece cobertura de código usando o --coveragesinalizador.

Para executar facilmente o Vitest com resultados de cobertura, gosto de incluir os seguintes itens na scriptseção de package.json :

{
  "name": "vue-weather-app",
  "version": "1.0.0",
  "scripts": {
    ...
    "test:unit": "vitest run --environment jsdom",
    "test:coverage": "vitest run --environment jsdom --coverage",
    "test:ui": "vitest --environment jsdom --coverage --ui"
  },
  ...
}

Quando npm run test:unité executado, os testes de unidade são executados:

$ npm run test:unit
...
 ✓ src/components/__tests__/WeatherBanner.spec.js (5)
 ✓ src/components/__tests__/WeatherSearch.spec.js (5)
 ✓ src/components/__tests__/WeatherFooter.spec.js (1)
 ✓ src/components/__tests__/WeatherHeader.spec.js (1)
 ✓ src/components/__tests__/WeatherResult.spec.js (3)
 ✓ src/components/__tests__/App.spec.js (7)

Test Files  6 passed (6)
     Tests  22 passed (22)
      Time  2.81s (in thread 536ms, 524.89%)

Quando npm run test:coverageexecutado, os testes de unidade são executados e a cobertura é relatada:

$ npm run test:coverage
...
Test Files  6 passed (6)
     Tests  22 passed (22)
      Time  3.38s (in thread 660ms, 512.10%)

--------------------|---------|----------|---------|---------|-------------------
File                | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------|---------|----------|---------|---------|-------------------
All files           |   99.15 |    96.55 |     100 |   99.15 |
 src                |   97.31 |    85.71 |     100 |   97.31 |
  App.vue           |   97.31 |    85.71 |     100 |   97.31 | 60-63
 src/components     |     100 |      100 |     100 |     100 |
  WeatherBanner.vue |     100 |      100 |     100 |     100 |
  WeatherFooter.vue |     100 |      100 |     100 |     100 |
  WeatherHeader.vue |     100 |      100 |     100 |     100 |
  WeatherResult.vue |     100 |      100 |     100 |     100 |
  WeatherSearch.vue |     100 |      100 |     100 |     100 |
--------------------|---------|----------|---------|---------|-------------------

O npm run test:uicarregará os resultados do teste e da cobertura em seu navegador da Web padrão, o que pode ser conveniente para ver visualmente onde está faltando a cobertura do teste.

Estrutura de teste de unidade

Depois de passar por vários arquivos de teste de unidade diferentes, recomendo a seguinte estrutura para o arquivo de teste de unidade para um componente Vue:

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { shallowMount } from '@vue/test-utils'
import App from '@/App.vue'  // Import Vue component to test
import axios from 'axios'    // Import libraries to mock

// Mock the axios library
vi.mock("axios", () => {
  return {
    default: {
      get: vi.fn(),
    },
  };
});


describe('Tests for the ... Component', () => {
  let wrapper = null

  beforeEach(() => {
    // set any initial data and create the mocks of libraries

    // render the component
    wrapper = shallowMount(App)
  })

  afterEach(() => {
    axios.get.mockReset()
    wrapper.unmount()
  })

  it('check successful events', () => { ... })

  it('check failure events', () => { ... })
})

Itens chave:

  • Cada suíte de teste de unidade tem um conjunto relacionado de testes de unidade
  • Utilize as funções beforeEach()e afterEach()para criar funções de teste de unidade independentes
  • Crie mocks de quaisquer bibliotecas usadas no componente Vue que foi testado
  • Renderize os componentes na beforeEach()função e atualize os dados da prop nas funções de teste de unidade
  • Use shallowMount()over mount()para se concentrar em testar componentes individuais

Conclusão

Este artigo fornece um guia para testes de unidade de componentes Vue, com foco em:

  1. Por que você deve escrever testes de unidade
  2. O que você deve (e não deve) testar a unidade
  3. Como escrever testes de unidade

Simplificando, ao considerar o que testar, concentre-se em testar as entradas e saídas (resultados reais), não a lógica de negócios subjacente (como os resultados são produzidos). Com isso em mente, reserve um ou dois minutos para revisar os exemplos novamente, anotando as entradas e saídas testadas.

Fonte:  https://testdrive.io

#vue 

What is GEEK

Buddha Community

Como e Guia para Testes Unitários Componentes Vue
Luna  Mosciski

Luna Mosciski

1600583123

8 Popular Websites That Use The Vue.JS Framework

In this article, we are going to list out the most popular websites using Vue JS as their frontend framework.

Vue JS is one of those elite progressive JavaScript frameworks that has huge demand in the web development industry. Many popular websites are developed using Vue in their frontend development because of its imperative features.

This framework was created by Evan You and still it is maintained by his private team members. Vue is of course an open-source framework which is based on MVVM concept (Model-view view-Model) and used extensively in building sublime user-interfaces and also considered a prime choice for developing single-page heavy applications.

Released in February 2014, Vue JS has gained 64,828 stars on Github, making it very popular in recent times.

Evan used Angular JS on many operations while working for Google and integrated many features in Vue to cover the flaws of Angular.

“I figured, what if I could just extract the part that I really liked about Angular and build something really lightweight." - Evan You

#vuejs #vue #vue-with-laravel #vue-top-story #vue-3 #build-vue-frontend #vue-in-laravel #vue.js

Como e Guia para Testes Unitários Componentes Vue

Este artigo serve como um guia para testes unitários de componentes Vue.

Veremos primeiro por que o teste de unidade é importante para a criação de software sustentável e o que você deve testar. Em seguida, detalharemos como:

  1. Criar e executar um teste de unidade para um componente Vue
  2. Testar diferentes aspectos de um componente Vue
  3. Use mocks para testar funções assíncronas
  4. Verifique a cobertura de código de seus testes de unidade
  5. Estruture seus arquivos de teste de unidade

Dependências :

O código-fonte (junto com instruções detalhadas de instalação) para o projeto Vue Weather App usado neste artigo pode ser encontrado no GitLab: Vue Weather App .

Objetivos

Ao final deste artigo, você deverá ser capaz de:

  1. Explique por que o teste de unidade é importante
  2. Descreva o que você deve (e não deve) testar a unidade
  3. Desenvolva um conjunto de testes de unidade para um componente Vue
  4. Execute os testes de unidade para um projeto Vue
  5. Utilize as funções beforeEach()e afterEach()dentro de um conjunto de testes de unidade
  6. Escreva testes de unidade para testar os detalhes de implementação de um componente Vue
  7. Escreva testes de unidade para testar os aspectos comportamentais de um componente Vue (eventos de clique, etc.)
  8. Explicar como a simulação pode ajudar a simplificar os testes de unidade
  9. Escreva testes de unidade para zombar de uma biblioteca e testar funções assíncronas
  10. Verifique a cobertura de código de seus testes de unidade
  11. Desenvolva um arquivo de teste de unidade bem estruturado para testar um componente Vue

Por que teste unitário?

Em geral, o teste ajuda a garantir que seu aplicativo funcione conforme o esperado para seus usuários finais.

Projetos de software com alta cobertura de teste nunca são perfeitos, mas é um bom indicador inicial da qualidade do software. Além disso, o código testável geralmente é um sinal de uma boa arquitetura de software, e é por isso que os desenvolvedores avançados levam os testes em consideração durante todo o ciclo de vida do desenvolvimento.

Os testes podem ser considerados em três níveis:

  1. Unidade
  2. Integração
  3. De ponta a ponta

Os testes de unidade testam a funcionalidade de uma unidade individual de código isolada de suas dependências. Eles são a primeira linha de defesa contra erros e inconsistências em sua base de código. O teste de unidade é uma parte fundamental do processo de Desenvolvimento Orientado a Testes (TDD).

O teste de unidade melhora a capacidade de manutenção do seu código.

Manutenibilidade refere-se a fazer correções de bugs ou aprimoramentos em seu código ou a outro desenvolvedor que precise atualizar seu código no futuro.

O teste de unidade deve ser combinado com um processo de Integração Contínua (CI) para garantir que seus testes de unidade sejam executados constantemente, idealmente em cada confirmação para seu repositório. Um conjunto sólido de testes de unidade pode ser fundamental para detectar defeitos rapidamente e no início do processo de desenvolvimento, antes que seus usuários finais os encontrem na produção.

O que testar

O que você deve testar? Ou, mais importante: O que você não deve testar?

Existem três tipos de testes a serem considerados com testes de unidade:

  1. Detalhes de implementação : a lógica de negócios subjacente que um componente usa para produzir um resultado com base em uma determinada entrada
  2. Interface pública/contrato de design : entradas específicas (ou adereços, neste caso) produzem resultados específicos
  3. Efeitos colaterais : "se isso, então aquilo"; por exemplo, quando um botão é clicado, algo acontece

Como você não pode testar todas as coisas, em que você deve se concentrar?

Concentre-se em testar entradas e saídas com as quais o usuário final irá interagir. A experiência que os usuários do seu produto têm é primordial!

  1. Entradas : dados, adereços, interação do usuário, métodos de ciclo de vida, armazenamento de dados ( Pinia / Vuex ), parâmetros de rota, strings de consulta
  2. Saídas : saída renderizada, eventos, resultados de dados, atualizações de armazenamento de dados (Pinia/Vuex), despachos

Ao se concentrar em testar as entradas/saídas de um módulo de software (por exemplo, um componente Vue), você está testando os principais aspectos que o usuário final experimentará.

Pode haver outra lógica interna em um módulo de software que seja complexa e precise ser testada na unidade, mas é mais provável que esses tipos de testes precisem de atualização durante uma refatoração de código.

Visão geral do aplicativo

Antes de discutir como testar a unidade dos componentes Vue, quero dar uma breve visão geral do aplicativo Vue Weather que testaremos.

Depois de clonar o repositório, instale as dependências e adicione a chave de API.

Revise o README do projeto para obter mais informações sobre como criar e adicionar a chave de API do Open Weather.

Uma vez feito, o aplicativo Vue Weather pode ser executado em seu computador local iniciando o servidor de desenvolvimento:

$ npm run dev

Depois que o aplicativo for criado, você verá uma mensagem de sucesso semelhante a:

vite v2.9.14 dev server running at:

 > Local: http://localhost:3000/
 > Network: use `--host` to expose

 ready in 543ms.

Neste ponto, o servidor de desenvolvimento estará funcionando. Você pode ver o aplicativo Vue navegando até http://localhost:3000 em seu navegador favorito. Quando você carrega o aplicativo pela primeira vez, nenhum dado é exibido; você verá apenas um campo de entrada para inserir a cidade para a qual deseja obter o clima:

Passo a passo do aplicativo Vue Weather - Etapa 1

Sem nenhum dado inserido, os botões 'Pesquisar' e 'Limpar' ficam desabilitados.

Assim que você começar a inserir dados para uma cidade (assim que o primeiro caractere for adicionado), os botões 'Pesquisar' e 'Limpar' serão ativados:

Passo a passo do aplicativo Vue Weather - Etapa 2

Se você clicar em 'Pesquisar' depois de inserir uma cidade válida, os dados meteorológicos serão exibidos para essa cidade:

Passo a passo do aplicativo Vue Weather - Etapa 3

Neste ponto, clicar no botão 'Clear Weather Data' fará com que os dados meteorológicos sejam apagados:

Passo a passo do aplicativo Vue Weather - Etapa 4

No entanto, a cidade que foi inserida permanecerá no campo de entrada.

Se você clicar em 'Limpar' agora, o campo de entrada será limpo e os botões 'Pesquisar' e 'Limpar' serão desativados novamente:

Passo a passo do aplicativo Vue Weather - Etapa 5

O que acontece se você inserir uma cidade inválida? E se você clicar em 'Limpar' antes de 'Limpar dados meteorológicos'? Quais outros estados do aplicativo você pode encontrar?

Teste de unidade em Vue

Como os componentes são os blocos de construção do Vue (e realmente de qualquer estrutura SPA ), eles são a parte mais crucial do seu aplicativo como um todo. Portanto, gaste a maior parte do seu tempo de teste alocado escrevendo testes de unidade que testam os componentes do seu aplicativo.

Com base na minha experiência com componentes Vue de teste de unidade, descobri que as ferramentas e a estrutura de teste - Vite e Vitest - satisfazem os principais aspectos de um bom ambiente de teste:

  • os testes são fáceis e divertidos de escrever
  • testes podem ser escritos rapidamente
  • testes podem ser executados com um único comando
  • os testes são executados rapidamente

Felizmente, você descobrirá que o teste de unidade de seus componentes Vue é uma experiência agradável, pois acho que isso é fundamental para incentivar mais testes.

Ferramentas de teste de unidade

Existem duas ferramentas que vamos usar para testes unitários dos componentes Vue:

  1. Vitest - estrutura de teste de unidade
  2. Vue Test Utils - biblioteca de utilitários de teste de unidade para Vue

Vitest é uma estrutura de teste de unidade que visa ser agnóstica de estrutura, para que possa ser usada para testar Vue, React, Svelte, Lit e outros projetos. O Vitest está configurado para ser executado com o Vite, o que resulta em uma execução de teste rápida .

Se você estiver familiarizado com o Jest, mudar para o Vitest é muito simples, pois a API do Vitest é compatível com o Jest.

Ao escrever testes de unidade para um componente Vue, Vitest e Vue Test Utils fornecem a seguinte funcionalidade:

engrenagem

  • Ferramenta de linha de comando para executar os testes e verificar a cobertura do teste
  • Interface do usuário para ver visualmente os resultados dos testes e os resultados da cobertura
  • Funções para escrever testes de unidade ( it, describe)
  • Funções para verificar os valores esperados ( expect, toMatch, toContain, etc.)
  • Zombando ( mockResolvedValue, mockRejectedValue)
  • Configuração ( beforeEach, beforeAll) / Desmontagem ( afterEach, afterAll)

Ver Utilitários de Teste

  • Componentes de montagem ( mount, shallowMount)
  • Configurando dados de adereços ( setProps)
  • Encontrando componentes HTML para teste ( findAll('h2'))
  • Utilitário para liberar todas as Promessas ( flushPromises())
  • Utilitário para acionar eventos de clique ( trigger)
  • Utilitário para verificar eventos emitidos ( emitted)

O Vitest fornece a funcionalidade genérica para escrever testes de unidade enquanto o Vue Test Utils fornece os utilitários de teste específicos do Vue.

Visão geral do teste de unidade

Para começar, vamos discutir a convenção de nomenclatura para testes de unidade no Vue. O arquivo de teste de unidade deve ter o seguinte formato:

  • <ComponentName>.spec.js

De acordo com o Guia de Estilo Vue , os nomes dos componentes devem ter várias palavras ( WeatherHeaderem vez de apenas Header) para evitar conflitos com elementos HTML.

Você normalmente deve ter um arquivo de teste de unidade para cada componente em seu projeto Vue. Dentro de cada arquivo de teste de unidade, pode haver um único conjunto de testes de unidade ou vários conjuntos de testes de unidade.

Os arquivos de teste de unidade devem ser colocados em uma subpasta dentro da pasta Componentes ( src/components/__tests__ ):

$ tree -d -L 2
.
├── node_modules
├── public
├── src
    ├── assets
    └── components
        └── __tests__

Executando os testes

O Vitest pode ser usado para executar os testes de unidade assim:

$ npm run test:unit

✓ src/components/__tests__/WeatherFooter.spec.js (1)
✓ src/components/__tests__/WeatherHeader.spec.js (1)
✓ src/components/__tests__/WeatherResult.spec.js (3)
✓ src/components/__tests__/WeatherBanner.spec.js (5)
✓ src/components/__tests__/WeatherSearch.spec.js (5)
✓ src/components/__tests__/App.spec.js (7)

Test Files  6 passed (6)
    Tests  22 passed (22)
     Time  2.38s (in thread 640ms, 371.13%)

Todos os comandos disponíveis para execução npmem seu projeto Vue são definidos no scriptscampo em package.json .

A configuração padrão do Vitest é executar os testes no modo de observação , o que significa que o conjunto de testes será executado novamente a cada salvamento em um arquivo aplicável. Para alterar essa configuração para que o Vitest seja executado apenas uma vez (sem "modo de observação"), atualize a test:unitconfiguração em package.json para incluir o runargumento:

"test:unit": "vitest run --environment jsdom",

Exemplos

Usando o aplicativo Vue Weather , vamos ver alguns exemplos para testar componentes Vue.

Exemplo 1 - Introdução ao Teste Unitário

Vamos pular direto para um exemplo de um arquivo de teste de unidade no Vue! O primeiro arquivo de teste de unidade é encontrado em src/components/__tests__/WeatherHeader.spec.js e testa o WeatherHeadercomponente:

import { describe, it, expect } from 'vitest'
import { shallowMount } from '@vue/test-utils'
import WeatherHeader from '../WeatherHeader.vue'


describe('WeatherHeader.vue Test', () => {
  it('renders message when component is created', () => {
    // render the component
    const wrapper = shallowMount(WeatherHeader, {
      propsData: {
        title: 'Vue Project'
      }
    })

    // check that the title is rendered
    expect(wrapper.text()).toMatch('Vue Project')
  })
})

Montagem

A primeira linha neste arquivo importa as funções de teste do Vitest que são usadas neste arquivo.

Se você quiser que a API Vitest esteja disponível globalmente (como o Jest funciona), adicione test: {globals: true}a defineConfig()função em vite.config.js . Para mais detalhes, confira a documentação do Vitest .

A segunda linha neste arquivo importa uma função chamada shallowMountda biblioteca Vue Test Utils. A ideia de 'montagem' significa o carregamento de um componente individual para poder testá-lo. Existem dois métodos para isso no Vue Test Utils:

  • shallowMount()- cria um wrapperpara o componente Vue, mas com componentes filhos stubbed
  • mount()- cria um wrapperpara o componente Vue, incluindo a montagem de qualquer componente filho

Como nosso foco é testar um componente individual (o WeatherHeadercomponente), usaremos shallowMount().

shallowMount()é melhor para testar um componente individual isoladamente, pois os componentes filhos são stubs. Esta é a situação ideal para testes unitários .

Além disso, usar shallowMount()para testar um componente com muitos componentes filho pode melhorar o tempo de execução do teste de unidade, pois não há custo (em termos de tempo) para renderizar os componentes filhos.

mount()é útil quando você deseja incluir o teste do comportamento dos componentes filho.

A terceira linha importa o componente Vue que será testado, WeatherHeader.vue .

Descrever Blocos

Após as importinstruções, há um describebloco que define um conjunto de testes de unidade.

Dentro de um arquivo de teste de unidade, pode haver vários describeblocos que definem diferentes conjuntos de testes de unidade. Da mesma forma, cada describebloco pode conter vários testes de unidade, onde cada teste de unidade é definido por um itbloco.

Considero essa distinção como:

  • describebloco - suíte de teste de unidade
  • itbloco - função de teste de unidade individual

O que é bom sobre o teste de unidade com o Vitest é que existem vários incentivos embutidos para adicionar comentários. Por exemplo, o primeiro argumento para describedeve explicar claramente qual componente Vue está sendo testado:

describe('WeatherHeader.vue Test', () => { ... })

Para cada itbloco, o primeiro argumento é uma descrição da função de teste, que deve ser uma breve descrição do que esse teste específico está fazendo. No exemplo acima, o itbloco testa se o componente 'renderiza a mensagem quando o componente é criado'.

Espera

Quanto ao teste unitário real, o primeiro passo é montar o componente Vue para que possa ser testado:

// render the component
const wrapper = shallowMount(WeatherHeader, {
  propsData: {
    title: 'Vue Project'
  }
})

A shallowMountfunção retorna um wrapperobjeto, que contém o componente montado e os métodos para testar esse componente. O wrapperobjeto nos permite testar todos os aspectos do HTML gerado pelo componente Vue e todas as propriedades (como dados) do componente Vue.

Além disso, as props, passadas para o WeatherHeadercomponente, são passadas como o segundo argumento para shallowMount().

A verificação real realizada no teste de unidade é:

// check that the title is rendered
expect(wrapper.text()).toMatch('Vue Project')

Esta linha serve wrapperpara verificar se o título gerado pelo componente é 'Vue Project'. Como essa verificação faz uma comparação de strings, é recomendável usar toMatch().

Auxiliares de teste

Enquanto as verificações no arquivo de teste de unidade para o WeatherHeadercomponente estão apenas verificando valores de string, existem [muitas opções disponíveis no Vitest para realizar verificações:

  • Booleanos:
    • toBeTruthy()- verifica se uma variável/instrução é verdadeira
    • toBeFalsy()- verifica se uma variável/instrução é falsa
  • Definiram:
    • toBeNull()- verifica se uma variável corresponde apenas a null
    • toBeUndefined()- verifica se uma variável não está definida
    • toBeDefined()- verifica se uma variável está definida
  • Números:
    • toBeGreaterThan()- verifica se um número é maior que o valor especificado
    • toBeGreaterThanOrEqual()- verifica se um número é maior ou igual ao valor especificado
    • toBeLessThan()- verifica se um número é menor que o valor especificado
    • toBeLessThanOrEqual()- verifica se um número é menor ou igual ao valor especificado
    • toBe()e toEqual()- verifica se um número é igual ao valor especificado (essas funções são equivalentes para números)
    • toBeCloseTo()- verifica se um número é igual ao valor especificado dentro de uma pequena tolerância (útil para números de ponto flutuante)
  • Cordas:
    • toMatch()- verifica se uma string é igual ao valor especificado (Regex pode ser usado como o valor especificado!)
  • Matrizes:
    • toContain()- verifica se um array contém o valor especificado

Além disso, o notqualificador pode ser usado com a maioria destas verificações:

expect(wrapper.text()).not.toMatch('Node Project')

Para obter uma lista completa de verificações disponíveis, confira a referência da API Vitest .

Exemplo 2 - Testando as Condições Iniciais

Este exemplo mostra como testar as condições iniciais (ou estado) do WeatherResultcomponente.

Aqui está um esboço do arquivo de teste de unidade (definido em src/components/__tests__/WeatherResult.spec.js ):

import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { shallowMount, flushPromises } from '@vue/test-utils'
import WeatherResult from '@/components/WeatherResult.vue'


describe('WeatherResult.vue Implementation Test', () => {
  let wrapper = null

  // SETUP - run before to each unit test
  beforeEach(() => {
    // render the component
    wrapper = shallowMount(WeatherResult, {
      propsData: {
        city: '',
        weatherSummary: '',
        weatherDescription: '',
        currentTemperature: 0.0,
        lowTemperature: 0.0,
        highTemperature: 0.0
      }
    })
  })

  // TEARDOWN - run after to each unit test
  afterEach(() => {
    wrapper.unmount()
  })

  it('initializes with correct elements', () => { ... })

  it('processes valid props data', async () => { ... })

  it('emits a custom event when the Clear Weather Data button is clicked', () => { ... })
})

O arquivo de teste de unidade utiliza a shallowMount()função para renderizar o WeatherResultcomponente, pois esse componente é testado como um componente individual isoladamente.

blocos antes de cada e depois de cada

Dentro do conjunto de testes de unidade (definido dentro do describebloco), existem duas novas funções definidas:

  • beforeEach()- chamado antes da execução de cada teste de unidade dentro deste conjunto de testes de unidade
  • afterEach()- chamado após a execução de cada teste de unidade dentro deste conjunto de testes de unidade

A beforeEach()função é usada para definir um estado consistente antes de executar cada teste de unidade. Esse conceito é muito importante para garantir que a ordem de execução dos testes unitários não afete os resultados dos testes unitários como um todo.

Neste exemplo, a beforeEach()função renderiza o componente com um conjunto padrão de dados de prop:

// SETUP - run before to each unit test
beforeEach(() => {
  // render the component
  wrapper = shallowMount(WeatherResult, {
    propsData: {
      city: '',
      weatherSummary: '',
      weatherDescription: '',
      currentTemperature: 0.0,
      lowTemperature: 0.0,
      highTemperature: 0.0
    }
  })
})

A afterEach()função é usada para limpar qualquer processamento realizado durante o teste de unidade.

Neste exemplo, a afterEach()função desmonta o wrapperusado durante o teste de unidade, para que wrapperpossa ser reinicializado para o próximo teste de unidade no beforeEach():

// TEARDOWN - run after to each unit test
afterEach(() => {
  wrapper.unmount()
})

Se você deseja executar o código que é executado antes ou depois da execução do conjunto geral de testes de unidade, você pode usar:

beforeAll(() => {
  /* Runs before all tests */
})
afterAll(() => {
  /* Runs after all tests */
})

Espera

O primeiro teste de unidade verifica as condições iniciais do WeatherResultcomponente:

it('initializes with correct elements', () => {
  // check that the heading text is rendered
  expect(wrapper.findAll('h2').length).toEqual(2)
  expect(wrapper.findAll('h2').at(0).text()).toMatch('Weather Summary')
  expect(wrapper.findAll('h2').at(1).text()).toMatch('Temperatures')

  // check that 6 fields of data for the temperature are displayed
  expect(wrapper.findAll('p').length).toEqual(6)
  expect(wrapper.findAll('p').at(0).text()).toMatch('City:')
  expect(wrapper.findAll('p').at(1).text()).toMatch('Summary:')
  expect(wrapper.findAll('p').at(2).text()).toMatch('Details:')
  expect(wrapper.findAll('p').at(3).text()).toMatch('Current: 0° F')
  expect(wrapper.findAll('p').at(4).text()).toMatch('High (Today): 0° F')
  expect(wrapper.findAll('p').at(5).text()).toMatch('Low (Today): 0° F')
})

Verificações:

  1. A primeira seção de expects verifica se os dois cabeçalhos (definidos como h2elementos) estão conforme o esperado.
  2. A segunda seção de expects verifica se os seis campos de dados (definidos como pelementos) estão conforme o esperado.

Exemplo 3 - Adereços de Teste

O segundo teste de unidade verifica se os dados válidos passados ​​como dados prop são tratados corretamente pelo WeatherResultcomponente:

it('processes valid props data', async () => {
  // Update the props passed in to the WeatherResult component
  wrapper.setProps({
    city: 'Chicago',
    weatherSummary: 'Cloudy',
    weatherDescription: 'Cloudy with a chance of rain',
    currentTemperature: 45.1,
    lowTemperature: 42.0,
    highTemperature: 47.7
  })

  // Wait until the DOM updates
  await flushPromises()

  // check that the prop data is stored as expected within the component
  expect(wrapper.vm.city).toMatch('Chicago')
  expect(wrapper.vm.weatherSummary).toMatch('Cloudy')
  expect(wrapper.vm.weatherDescription).toMatch('Cloudy with a chance of rain')
  expect(wrapper.vm.currentTemperature).toEqual(45.1)
  expect(wrapper.vm.lowTemperature).toBeCloseTo(42.0)
  expect(wrapper.vm.highTemperature).toBe(47.7)

  // check that the heading text is rendered
  expect(wrapper.findAll('h2').length).toEqual(2)
  expect(wrapper.findAll('h2').at(0).text()).toMatch('Weather Summary')
  expect(wrapper.findAll('h2').at(1).text()).toMatch('Temperatures')

  // check that 6 fields of data for the temperature are displayed
  expect(wrapper.findAll('p').length).toEqual(6)
  expect(wrapper.findAll('p').at(0).text()).toMatch('City: Chicago')
  expect(wrapper.findAll('p').at(1).text()).toMatch('Summary: Cloudy')
  expect(wrapper.findAll('p').at(2).text()).toMatch('Details: Cloudy with a chance of rain')
  expect(wrapper.findAll('p').at(3).text()).toMatch('Current: 45.1° F')
  expect(wrapper.findAll('p').at(4).text()).toMatch('High (Today): 47.7° F')
  expect(wrapper.findAll('p').at(5).text()).toMatch('Low (Today): 42° F')
})

Como a beforeEach()função fornece um conjunto padrão de dados de prop, precisamos substituir os dados de prop usando a setProps()função.

Para garantir que os dados do prop causem as atualizações esperadas no WeatherResult, o teste precisa aguardar que todas as atualizações do DOM entrem em vigor:

// Wait until the DOM updates
await flushPromises()

NOTA: O uso de awaitsó é possível se a função for definida com async!

Verificações:

  1. Com os dados de props atualizados, podemos verificar se os dados de props foram armazenados corretamente no WeatherResultcomponente verificando os elementos de dados (usando wrapper.vm).
  2. A segunda seção de expects verifica se os dois cabeçalhos (definidos como h2elementos) estão conforme o esperado.
  3. O último conjunto de expects verifica se os dados da prop são usados ​​para definir os seis campos de dados (definidos como pelementos) conforme o esperado.

Exemplo 4 - Testando a entrada do usuário (Evento de clique)

O terceiro teste de unidade verifica se o clear-weather-dataevento é emitido pelo WeatherResultcomponente quando o usuário clica no botão 'Clear Weather Data':

it('emits a custom event when the Clear Weather Data button is clicked', () => {
  // trigger an event when the 'Clear Weather Data' button is clicked
  wrapper.findAll('button').at(0).trigger('click')

  // check that 1 occurrence of the event has been emitted
  expect(wrapper.emitted('clear-weather-data')).toBeTruthy()
  expect(wrapper.emitted('clear-weather-data').length).toBe(1)
})

Para acionar um evento de clique, o buttonelemento deve ser encontrado no wrappere então a triggerfunção é chamada para acionar o evento de clique.

Uma vez que o botão é clicado, o teste de unidade verifica se apenas um evento personalizado (com o nome de clear-weather-data) é emitido.

Exemplos de zombaria

Dentro do Appcomponente, quando um usuário pesquisa o clima de uma cidade, uma chamada HTTP GET é feita para o Open Weather para recuperar os dados por meio de uma biblioteca de terceiros chamada Axios :

const searchCity = (inputCity) => {
  // GET request for user data
  axios.get('http://api.openweathermap.org/data/2.5/weather?q=' + inputCity + '&units=imperial&APPID=' + openweathermapApiKey.value)
    .then((response) => {
      // handle success
      console.log(response)

      weatherData.value.city = response.data.name
      weatherData.value.weatherSummary = response.data.weather[0].main
      weatherData.value.weatherDescription = response.data.weather[0].description
      weatherData.value.currentTemperature = response.data.main.temp
      weatherData.value.lowTemperature = response.data.main.temp_min
      weatherData.value.highTemperature = response.data.main.temp_max
      validWeatherData.value = true
    })
    .catch((error) => {
      // handle error
      messageType.value = 'Error'
      messageToDisplay.value = 'ERROR! Unable to retrieve weather data for ' + inputCity + '!'
      console.log(error.message)
      resetData()
    })
    .finally((response) => {
      // always executed
      console.log('HTTP GET Finished!')
    })
}

Ao pensar em como testar as chamadas HTTP GET, dois cenários vêm à mente, cada um testando o efeito colateral da chamada real da API:

  • Resposta HTTP GET bem-sucedida ( caminho feliz )
  • Falha na resposta HTTP GET ( caminho de exceção )

Ao testar o código que utiliza uma API externa, geralmente é mais fácil não fazer a chamada real, substituindo a chamada por uma simulação. Existem prós e contras nessa abordagem, no entanto.

Prós:

  1. Os testes não dependerão de uma solicitação de rede
  2. Eles não vão quebrar se a API cair
  3. Eles vão correr muito mais rápido

Contras:

  1. Você precisará atualizar os testes sempre que o esquema da API for alterado
  2. É difícil acompanhar as mudanças de API em uma arquitetura de microsserviço
  3. A simulação é um conceito difícil de entender e pode adicionar muita confusão aos seus conjuntos de testes

Em algum momento, você deve verificar a integração completa para garantir que a forma da resposta da API não foi alterada.

Como este artigo é focado em testes de unidade, vamos zombar da biblioteca Axios.

A simulação fornece um meio de imitar o comportamento esperado de um módulo de software. Embora o mocking possa ser usado no código de produção (muito perigoso!), normalmente é usado durante o desenvolvimento e teste.

O carregamento de dados de uma API externa leva tempo. Embora normalmente leve menos de um ou dois segundos para carregar os dados do Open Weather neste aplicativo, outras APIs externas podem consumir mais tempo. Além disso, queremos uma maneira de verificar facilmente se a solicitação HTTP GET falha. Portanto, adicionaremos simulações para especificar como a solicitação GET responderia.

Exemplo 5 - Teste de código assíncrono (caso de sucesso)

Os testes de unidade para o Appcomponente estão localizados no arquivo src/components/__tests__/App.spec.js .

Para começar, precisamos importda biblioteca Axios:

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { shallowMount, mount, flushPromises } from '@vue/test-utils'
import App from '../../App.vue'
import axios from 'axios'

No entanto, não queremos usar a biblioteca Axios real como é feito no código-fonte ( App.vue ). Em vez disso, queremos criar uma simulação da biblioteca Axios para que não chamemos a API externa:

// Mock the axios library
vi.mock("axios", () => {
  return {
    default: {
      get: vi.fn(),
    },
  };
});

Este bloco de código diz ao Vitest que o get()método dentro da axiosbiblioteca deve ser simulado.

No arquivo de teste de unidade ( src/components/__tests__/App.spec.js ), para o Appcomponente, temos:

  1. Simulando uma solicitação HTTP GET bem-sucedida
  2. Simulando uma solicitação HTTP GET com falha

Para começar, vamos lidar com a situação nominal em que a solicitação HTTP GET é bem-sucedida.

describe('Implementation Test for App.vue with Successful HTTP GET', () => {
  let wrapper = null

  beforeEach(() => {
    const responseGet = { data:
      {
        name: 'Chicago',
        weather: [
          {
            main: 'Cloudy',
            description: 'Cloudy with a chance of rain'
          }
        ],
        main: {
          temp: 56.3,
          temp_min: 53.8,
          temp_max: 58.6
        }
      }
    }

    // Set the mock call to GET to return a successful GET response
    axios.get.mockResolvedValue(responseGet)

    // render the component
    wrapper = shallowMount(App)
  })

  afterEach(() => {
    axios.get.mockReset()
    wrapper.unmount()
  })

  ...

})

blocos antes de cada e depois de cada

Na beforeEach()função, definimos a resposta que deve ocorrer quando axios.get()for chamado. A resposta são os dados meteorológicos pré-enlatados que se parecem com o que obtemos do Open Weather, se realmente fizermos a solicitação. A linha chave nesta seção é:

// Set the mock call to GET to return a successful GET response
axios.get.mockResolvedValue(responseGet)

Esta linha é a chave, pois permite ao Vitest saber que deve retornar o responseGetarray quando axios.get()for chamado no Appcomponente, em vez de realmente fazer uma chamada HTTP GET usando o Axios.

A próxima seção do conjunto de testes de unidade define a afterEach()função:

afterEach(() => {
  axios.get.mockReset()
  wrapper.unmount()
})

A afterEach()função, que é chamada após a execução de cada teste de unidade, limpa a simulação axios.get()que foi criada durante a execução do teste de unidade. Essa abordagem é uma boa prática para limpar quaisquer simulações após a execução de um teste, para que todos os testes subsequentes comecem a partir de um estado conhecido.

Espera

Agora podemos definir o teste de unidade:

it('does load the weather data when a successful HTTP GET occurs', async () => {
  wrapper.vm.searchCity('Chicago')

  // Wait until the DOM updates
  await flushPromises()

  expect(axios.get).toHaveBeenCalledTimes(1)
  expect(axios.get).toBeCalledWith(expect.stringMatching(/Chicago/))

  // check that the user data is properly set
  expect(wrapper.vm.weatherData.city).toMatch('Chicago')
  expect(wrapper.vm.weatherData.weatherSummary).toMatch('Cloudy')
  expect(wrapper.vm.weatherData.weatherDescription).toMatch('Cloudy with a chance of rain')
  expect(wrapper.vm.weatherData.currentTemperature).toEqual(56.3)
  expect(wrapper.vm.weatherData.lowTemperature).toEqual(53.8)
  expect(wrapper.vm.weatherData.highTemperature).toEqual(58.6)
  expect(wrapper.vm.validWeatherData).toBe(true)
})

Como já passamos pelas etapas de definição do mock e renderização do componente (via shallowMount()), este teste de unidade pode se concentrar em realizar verificações.

O teste de unidade começa chamando a searchCity()função:

wrapper.vm.searchCity('Chicago')

Para garantir que a searchCity()função cause as atualizações esperadas no Appcomponente, o teste precisa aguardar que todas as Promises sejam resolvidas e que as atualizações do DOM entrem em vigor:

// Wait until all Promises are resolved and the DOM updates
await flushPromises()

Verificamos que axios.get()foi chamado apenas uma vez e que a chamada HTTP GET incluiu o nome correto da cidade:

expect(axios.get).toHaveBeenCalledTimes(1)
expect(axios.get).toBeCalledWith(expect.stringMatching(/Chicago/))

Para ser muito completo, os dados meteorológicos também verificam a instância do Appcomponente renderizado neste teste de unidade para garantir que ele corresponda aos dados pré-enlatados retornados da simulação de axios.get():

// check that the user data is properly set
expect(wrapper.vm.weatherData.city).toMatch('Chicago')
expect(wrapper.vm.weatherData.weatherSummary).toMatch('Cloudy')
expect(wrapper.vm.weatherData.weatherDescription).toMatch('Cloudy with a chance of rain')
expect(wrapper.vm.weatherData.currentTemperature).toEqual(56.3)
expect(wrapper.vm.weatherData.lowTemperature).toEqual(53.8)
expect(wrapper.vm.weatherData.highTemperature).toEqual(58.6)
expect(wrapper.vm.validWeatherData).toBe(true)

Exemplo 6 - Teste de código assíncrono (caso de falha)

Embora seja bom testar quando as coisas acontecem conforme o esperado, também é importante verificar como nosso software reage a condições negativas. Com isso em mente, vamos criar um segundo conjunto de testes de unidade para verificar uma solicitação HTTP GET com falha:

describe('Implementation Test for App.vue with Failed HTTP GET', () => { ... })

Não há problema em ter vários conjuntos de testes de unidade em um único arquivo de teste de unidade ( .spec.js ).

Como os mocks são criados na beforeEach()função, é necessário haver conjuntos de testes de unidade separados com beforeEach()implementações diferentes para respostas HTTP GET bem-sucedidas e com falha.

blocos antes de cada e depois de cada

A beforeEach()função neste conjunto de testes de unidade é bem diferente:

beforeEach(() => {
  // Set the mock call to GET to return a failed GET request
  axios.get.mockRejectedValue(new Error('BAD REQUEST'))

  // Render the component
  wrapper = shallowMount(App)
})

Em vez de definir uma resposta para retornar da axios.get()chamada, agora estamos retornando um Promiseobjeto com falha com a resposta de 'BAD REQUEST'.

A afterEach()função para este conjunto de testes de unidade é idêntica ao outro conjunto de testes de unidade:

afterEach(() => {
  axios.get.mockReset()
  wrapper.unmount()
})

Espera

Aqui está a função de teste de unidade para testar uma solicitação HTTP GET com falha:

it('does not load the weather data when a failed HTTP GET occurs', async () => {
  wrapper.vm.searchCity('Chicago')

  expect(axios.get).toHaveBeenCalledTimes(1)
  expect(axios.get).toBeCalledWith(expect.stringMatching(/Chicago/))

  // Wait until the DOM updates
  await flushPromises()

  // Check that there is no user data loaded when the GET request fails
  expect(wrapper.vm.weatherData.city).toMatch(/^$/)
  expect(wrapper.vm.weatherData.weatherSummary).toMatch(/^$/)
  expect(wrapper.vm.weatherData.weatherDescription).toMatch(/^$/)
  expect(wrapper.vm.weatherData.currentTemperature).toEqual(0)
  expect(wrapper.vm.weatherData.lowTemperature).toEqual(0)
  expect(wrapper.vm.weatherData.highTemperature).toEqual(0)
  expect(wrapper.vm.validWeatherData).toBe(false)

  // check that the banner message indicates failure
  expect(wrapper.vm.messageToDisplay).toMatch('ERROR! Unable to retrieve weather data for Chicago!')
  expect(wrapper.vm.messageType).toMatch('Error')
})

Assim como no conjunto de testes de unidade anterior, verificamos se apenas uma instância de axios.get()é chamada e, em seguida, verificamos se nenhum dado climático foi carregado no Appcomponente.

Cobertura de código

Ao desenvolver testes de unidade, pode ser bom entender quanto do código-fonte é realmente testado. Esse conceito é conhecido como cobertura de código.

Preciso deixar bem claro que ter um conjunto de testes de unidade que cobrem 100% do código-fonte não é de forma alguma um indicador de que o código foi testado adequadamente.

Essa métrica significa que há muitos testes de unidade e muito esforço foi feito para desenvolver os testes de unidade. A qualidade dos testes unitários ainda precisa ser verificada por inspeção de código.

O outro extremo onde este é um conjunto mínimo (ou nenhum!) de testes de unidade é um indicador muito ruim.

Vitest fornece cobertura de código usando o --coveragesinalizador.

Para executar facilmente o Vitest com resultados de cobertura, gosto de incluir os seguintes itens na scriptseção de package.json :

{
  "name": "vue-weather-app",
  "version": "1.0.0",
  "scripts": {
    ...
    "test:unit": "vitest run --environment jsdom",
    "test:coverage": "vitest run --environment jsdom --coverage",
    "test:ui": "vitest --environment jsdom --coverage --ui"
  },
  ...
}

Quando npm run test:unité executado, os testes de unidade são executados:

$ npm run test:unit
...
 ✓ src/components/__tests__/WeatherBanner.spec.js (5)
 ✓ src/components/__tests__/WeatherSearch.spec.js (5)
 ✓ src/components/__tests__/WeatherFooter.spec.js (1)
 ✓ src/components/__tests__/WeatherHeader.spec.js (1)
 ✓ src/components/__tests__/WeatherResult.spec.js (3)
 ✓ src/components/__tests__/App.spec.js (7)

Test Files  6 passed (6)
     Tests  22 passed (22)
      Time  2.81s (in thread 536ms, 524.89%)

Quando npm run test:coverageexecutado, os testes de unidade são executados e a cobertura é relatada:

$ npm run test:coverage
...
Test Files  6 passed (6)
     Tests  22 passed (22)
      Time  3.38s (in thread 660ms, 512.10%)

--------------------|---------|----------|---------|---------|-------------------
File                | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------|---------|----------|---------|---------|-------------------
All files           |   99.15 |    96.55 |     100 |   99.15 |
 src                |   97.31 |    85.71 |     100 |   97.31 |
  App.vue           |   97.31 |    85.71 |     100 |   97.31 | 60-63
 src/components     |     100 |      100 |     100 |     100 |
  WeatherBanner.vue |     100 |      100 |     100 |     100 |
  WeatherFooter.vue |     100 |      100 |     100 |     100 |
  WeatherHeader.vue |     100 |      100 |     100 |     100 |
  WeatherResult.vue |     100 |      100 |     100 |     100 |
  WeatherSearch.vue |     100 |      100 |     100 |     100 |
--------------------|---------|----------|---------|---------|-------------------

O npm run test:uicarregará os resultados do teste e da cobertura em seu navegador da Web padrão, o que pode ser conveniente para ver visualmente onde está faltando a cobertura do teste.

Estrutura de teste de unidade

Depois de passar por vários arquivos de teste de unidade diferentes, recomendo a seguinte estrutura para o arquivo de teste de unidade para um componente Vue:

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { shallowMount } from '@vue/test-utils'
import App from '@/App.vue'  // Import Vue component to test
import axios from 'axios'    // Import libraries to mock

// Mock the axios library
vi.mock("axios", () => {
  return {
    default: {
      get: vi.fn(),
    },
  };
});


describe('Tests for the ... Component', () => {
  let wrapper = null

  beforeEach(() => {
    // set any initial data and create the mocks of libraries

    // render the component
    wrapper = shallowMount(App)
  })

  afterEach(() => {
    axios.get.mockReset()
    wrapper.unmount()
  })

  it('check successful events', () => { ... })

  it('check failure events', () => { ... })
})

Itens chave:

  • Cada suíte de teste de unidade tem um conjunto relacionado de testes de unidade
  • Utilize as funções beforeEach()e afterEach()para criar funções de teste de unidade independentes
  • Crie mocks de quaisquer bibliotecas usadas no componente Vue que foi testado
  • Renderize os componentes na beforeEach()função e atualize os dados da prop nas funções de teste de unidade
  • Use shallowMount()over mount()para se concentrar em testar componentes individuais

Conclusão

Este artigo fornece um guia para testes de unidade de componentes Vue, com foco em:

  1. Por que você deve escrever testes de unidade
  2. O que você deve (e não deve) testar a unidade
  3. Como escrever testes de unidade

Simplificando, ao considerar o que testar, concentre-se em testar as entradas e saídas (resultados reais), não a lógica de negócios subjacente (como os resultados são produzidos). Com isso em mente, reserve um ou dois minutos para revisar os exemplos novamente, anotando as entradas e saídas testadas.

Fonte:  https://testdrive.io

#vue 

Tamia  Walter

Tamia Walter

1596754901

Testing Microservices Applications

The shift towards microservices and modular applications makes testing more important and more challenging at the same time. You have to make sure that the microservices running in containers perform well and as intended, but you can no longer rely on conventional testing strategies to get the job done.

This is where new testing approaches are needed. Testing your microservices applications require the right approach, a suitable set of tools, and immense attention to details. This article will guide you through the process of testing your microservices and talk about the challenges you will have to overcome along the way. Let’s get started, shall we?

A Brave New World

Traditionally, testing a monolith application meant configuring a test environment and setting up all of the application components in a way that matched the production environment. It took time to set up the testing environment, and there were a lot of complexities around the process.

Testing also requires the application to run in full. It is not possible to test monolith apps on a per-component basis, mainly because there is usually a base code that ties everything together, and the app is designed to run as a complete app to work properly.

Microservices running in containers offer one particular advantage: universal compatibility. You don’t have to match the testing environment with the deployment architecture exactly, and you can get away with testing individual components rather than the full app in some situations.

Of course, you will have to embrace the new cloud-native approach across the pipeline. Rather than creating critical dependencies between microservices, you need to treat each one as a semi-independent module.

The only monolith or centralized portion of the application is the database, but this too is an easy challenge to overcome. As long as you have a persistent database running on your test environment, you can perform tests at any time.

Keep in mind that there are additional things to focus on when testing microservices.

  • Microservices rely on network communications to talk to each other, so network reliability and requirements must be part of the testing.
  • Automation and infrastructure elements are now added as codes, and you have to make sure that they also run properly when microservices are pushed through the pipeline
  • While containerization is universal, you still have to pay attention to specific dependencies and create a testing strategy that allows for those dependencies to be included

Test containers are the method of choice for many developers. Unlike monolith apps, which lets you use stubs and mocks for testing, microservices need to be tested in test containers. Many CI/CD pipelines actually integrate production microservices as part of the testing process.

Contract Testing as an Approach

As mentioned before, there are many ways to test microservices effectively, but the one approach that developers now use reliably is contract testing. Loosely coupled microservices can be tested in an effective and efficient way using contract testing, mainly because this testing approach focuses on contracts; in other words, it focuses on how components or microservices communicate with each other.

Syntax and semantics construct how components communicate with each other. By defining syntax and semantics in a standardized way and testing microservices based on their ability to generate the right message formats and meet behavioral expectations, you can rest assured knowing that the microservices will behave as intended when deployed.

Ways to Test Microservices

It is easy to fall into the trap of making testing microservices complicated, but there are ways to avoid this problem. Testing microservices doesn’t have to be complicated at all when you have the right strategy in place.

There are several ways to test microservices too, including:

  • Unit testing: Which allows developers to test microservices in a granular way. It doesn’t limit testing to individual microservices, but rather allows developers to take a more granular approach such as testing individual features or runtimes.
  • Integration testing: Which handles the testing of microservices in an interactive way. Microservices still need to work with each other when they are deployed, and integration testing is a key process in making sure that they do.
  • End-to-end testing: Which⁠—as the name suggests⁠—tests microservices as a complete app. This type of testing enables the testing of features, UI, communications, and other components that construct the app.

What’s important to note is the fact that these testing approaches allow for asynchronous testing. After all, asynchronous development is what makes developing microservices very appealing in the first place. By allowing for asynchronous testing, you can also make sure that components or microservices can be updated independently to one another.

#blog #microservices #testing #caylent #contract testing #end-to-end testing #hoverfly #integration testing #microservices #microservices architecture #pact #testing #unit testing #vagrant #vcr

Teresa  Bosco

Teresa Bosco

1598685221

Vue File Upload Using vue-dropzone Tutorial

In this tutorial, I will show you how to upload a file in Vue using vue-dropzone library. For this example, I am using Vue.js 3.0. First, we will install the Vue.js using Vue CLI, and then we install the vue-dropzone library. Then configure it, and we are ready to accept the file. DropzoneJS is an open source library that provides drag and drops file uploads with image previews. DropzoneJS is lightweight doesn’t depend on any other library (like jQuery) and is  highly customizable. The  vue-dropzone is a vue component implemented on top of Dropzone.js. Let us start Vue File Upload Using vue-dropzone Tutorial.

Dropzone.js is an open-source library providing drag-and-drop file uploads with image previews. DropzoneJS is lightweight, doesn’t depend on any other library (like jQuery), and is highly customizable.

The vue-dropzone is a vue component implemented on top of Dropzone.js.

First, install the Vue using Vue CLI.

Step 1: Install Vue.js using Vue CLI.

Go to your terminal and hit the following command.

npm install -g @vue/cli
         or
yarn global add @vue/cli

If you face any error, try running the command as an administrator.

Now, we need to generate the necessary scaffold. So type the following command.

vue create vuedropzone

It will install the scaffold.

Open the project in your favorite editor. Mine is Visual Studio Code.

cd vuedropzone
code .

Step 2: Install vue-dropzone.

I am using the Yarn package manager. So let’s install using Yarn. You can use NPM, also. It does not matter.

yarn add vue2-dropzone

or

npm install vue2-dropzone

Okay, now we need to add one css file with the above package. Now, vue cli uses css loader, so we can directly import in the src >>  main.js entry file.

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

import 'vue2-dropzone/dist/vue2Dropzone.css'

If importing css is not working for you, then you need to install that CSS file manually.

Copy this vue2Dropzone.css file’s content.

Create one file inside the src  >>  assets folder, create one css file called vuedropzone.css and paste the content there.

Import this css file inside src  >>  App.vue file.

<style lang="css">
  @import './assets/vuedropzone.css';
</style>

Now, it should include in our application.

Step 3: Upload an Image.

Our primary boilerplate has one ready-made component called HelloWorld.vue inside src  >>  components folder. Now, create one more file called FileUpload.vue.

Add the following code to FileUpload.vue file.

// FileUpload.vue

<template>
  <div id="app">
    <vue-dropzone id="upload" :options="config"></vue-dropzone>
  </div>
</template>

<script>
import vueDropzone from "vue2-dropzone";

export default {
  data: () => ({
    config: {
      url: "https://appdividend.com"
    }
  }),
  components: {
    vueDropzone
  }
};
</script>

Here, our API endpoint is https://appdividend.com. It is the point where we will hit the POST route and store our image, but it is my blog’s homepage, so it will not work anyway. But let me import this file into App.vue component and see what happens.

// App.vue

<template>
  <div id="app">
    <FileUpload />
  </div>
</template>

<script>
import FileUpload from './components/FileUpload.vue'

export default {
  name: 'app',
  components: {
    FileUpload
  }
}
</script>

<style lang="css">
  @import './assets/vuedropzone.css';
</style>

Now, start the development server using the following command. It will open up URL: http://localhost:8080.

npm run serve

Now, after uploading the image, we can see that the image upload is failed due to the wrong POST request endpoint.

Step 4: Create Laravel API for the endpoint.

Install the Laravel.

After that, we configure the database in the .env file and use MySQL database.

We need to create one model and migration file to store the image. So let us install the following command inside the Laravel project.

php artisan make:model Image -m

It will create both the Image model and create_images_table.php migrations file.

Now, open the migrations file and add the schema to it.

// create_images_table.php

public function up()
    {
        Schema::create('images', function (Blueprint $table) {
            $table->increments('id');
            $table->string('image_name');
            $table->timestamps();
        });
    }

Now, migrate the database table using the following command.

php artisan migrate

It creates the table in the database.

Now, we need to add a laravel-cors package to prevent cross-site-allow-origin errors. Go to the Laravel root and enter the following command to install it.

composer require barryvdh/laravel-cors

Configure it in the config  >>  app.php file.

Barryvdh\Cors\ServiceProvider::class,

Add the middleware inside app >>  Http  >>  Kernel.php file.

// Kernel.php

protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
        \Barryvdh\Cors\HandleCors::class,
];

Step 5: Define the API route and method to store the image.

First, create an ImageController.php file using the following command.

php artisan make:controller ImageController

Define the store method. Also, create one images folder inside the public directory because we will store an image inside it.

Right now, I have written the store function that handles one image at a time. So do not upload multiple photos at a time; otherwise, it will break.

// ImageController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Image;

class ImageController extends Controller
{
    public function store(Request $request)
    {
       if($request->file('file'))
       {
          $image = $request->file('file');
          $name = time().$image->getClientOriginalName();
          $image->move(public_path().'/images/', $name); 
        }

       $image= new Image();
       $image->image_name = $name;
       $image->save();

       return response()->json(['success' => 'You have successfully uploaded an image'], 200);
     }
}

Go to the routes   >>  api.php file and add the following route.

// api.php

Route::post('image', 'ImageController@store');

Step 6: Edit FileUpload.vue component.

We need to add the correct Post request API endpoint in FileUpload.vue component.

// FileUpload.vue

<template>
  <div id="app">
    <vue-dropzone id="drop1" :options="config" @vdropzone-complete="afterComplete"></vue-dropzone>
  </div>
</template>

<script>
import vueDropzone from "vue2-dropzone";

export default {
  data: () => ({
    config: {
      url: "http://localhost:8000/api/image",
      
    }
  }),
  components: {
    vueDropzone
  },
  methods: {
    afterComplete(file) {
      console.log(file);
    }
  }
};
</script>

Now, save the file and try to upload an image. If everything is okay, then you will be able to save the image on the Laravel web server as well as save the name in the database as well.

You can also verify on the server side by checking the database entry and the images folder in which we have saved the image.

Step 7: More vue-dropzone configuration.

The only required options are url, but there are many more you can use.

For example, let’s say you want:

  • A maximum of 4 files
  • 2 MB max file size
  • Sent in chunks of 500 bytes
  • Set a custom thumbnail size of 150px
  • Make the uploaded items cancelable and removable (by default, they’re not)
export default {
  data: () => ({
    dropOptions: {
      url: "https://httpbin.org/post",
      maxFilesize: 5, // MB
      maxFiles: 5,
      chunking: true,
      chunkSize: 400, // Bytes
      thumbnailWidth: 100, // px
      thumbnailHeight: 100,
      addRemoveLinks: true
    }
  })
  // ...
}

Happy Coding !!!

Originally published at https://appdividend.com 

#vue #vue-dropzone #vue.js #dropzone.js #dropzonejs #vue cli

Software Testing 101: Regression Tests, Unit Tests, Integration Tests

Automation and segregation can help you build better software
If you write automated tests and deliver them to the customer, he can make sure the software is working properly. And, at the end of the day, he paid for it.

Ok. We can segregate or separate the tests according to some criteria. For example, “white box” tests are used to measure the internal quality of the software, in addition to the expected results. They are very useful to know the percentage of lines of code executed, the cyclomatic complexity and several other software metrics. Unit tests are white box tests.

#testing #software testing #regression tests #unit tests #integration tests