# Testes em Vue 3 em 2026: Vitest, Vue Test Utils e Perguntas de Entrevista > Um guia prático de testes em Vue para 2026: configurar o Vitest, montar componentes com o Vue Test Utils, testar composables e stores do Pinia, mockar APIs, medir cobertura e as perguntas de entrevista que as equipes de contratação realmente fazem. - Published: 2026-06-21 - Updated: 2026-07-06 - Author: SharpSkill - Tags: vue, testing, vitest, vue-test-utils, interview - Reading time: 10 min --- Testar em Vue é o que separa uma refatoração confiante de um chute frágil, e em 2026 o ferramental se consolidou em torno de duas bibliotecas: [Vitest](https://vitest.dev/guide/) como executor de testes e [Vue Test Utils](https://test-utils.vuejs.org/) para montar componentes. Este guia percorre a configuração da stack, os testes de componentes e composables, o mock de dependências e as respostas para as perguntas de entrevista sobre testes em Vue que as equipes de contratação de fato fazem. > **A stack moderna de testes em Vue em 2026** > > A recomendação padrão da [documentação do Vue](https://vuejs.org/guide/scaling-up/testing.html) é usar o Vitest para testes unitários e de componente, o Vue Test Utils como API de montagem de baixo nível e o Playwright ou o Cypress para cobertura ponta a ponta. O Vitest reaproveita a mesma configuração do Vite usada pela aplicação, então os testes rodam pelo mesmo pipeline de transformação, com hot reload praticamente instantâneo. ## Configurando o Vitest para um projeto Vue 3 O Vitest compartilha o pipeline do Vite, o que significa que um único arquivo de configuração comanda tanto o servidor de desenvolvimento quanto o executor de testes. A opção `environment` precisa ser definida como `jsdom` ou `happy-dom` para que os testes de componente tenham um DOM onde renderizar. A flag `globals: true` expõe `describe`, `it` e `expect` sem que seja preciso importá-los em cada arquivo. ```typescript // vitest.config.ts import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' import { fileURLToPath } from 'node:url' export default defineConfig({ plugins: [vue()], test: { // jsdom provides document/window so components can mount environment: 'jsdom', // expose describe/it/expect globally globals: true, // run this before each test file (matchers, cleanup) setupFiles: ['./tests/setup.ts'], // only pick up *.spec.ts and *.test.ts files include: ['**/*.{test,spec}.ts'], }, resolve: { alias: { // mirror the @ alias used across the app '@': fileURLToPath(new URL('./src', import.meta.url)), }, }, }) ``` Com o Vitest 3, a configuração acima já funciona de imediato após instalar `vitest`, `@vue/test-utils`, `@vitejs/plugin-vue` e `jsdom`. Como o alias espelha a configuração da aplicação, imports como `@/composables/useCart` são resolvidos de forma idêntica nos testes e em produção. ## Montando componentes com o Vue Test Utils O Vue Test Utils oferece dois pontos de entrada: `mount`, que renderiza a árvore completa de componentes, e `shallowMount`, que substitui os componentes filhos por stubs. Para a maioria dos testes unitários, o `mount` é preferível porque exercita o comportamento real de renderização. O `wrapper` retornado fornece utilitários de consulta como `find`, `get` e `text` para verificar a saída renderizada. ```typescript // PriceTag.spec.ts import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import PriceTag from '@/components/PriceTag.vue' describe('PriceTag', () => { it('formats the amount as currency', () => { const wrapper = mount(PriceTag, { // props are passed through the mounting options props: { amount: 4200, currency: 'EUR' }, }) // get() throws if the selector is missing, unlike find() expect(wrapper.get('[data-test="price"]').text()).toBe('€42.00') }) it('applies a discount class when on sale', () => { const wrapper = mount(PriceTag, { props: { amount: 4200, currency: 'EUR', onSale: true }, }) // classes() returns the array of applied CSS classes expect(wrapper.get('[data-test="price"]').classes()).toContain('price--sale') }) }) ``` Mirar nos elementos por atributos `data-test` em vez de classes CSS mantém os testes resilientes: uma mudança de estilo em `.price--sale` não quebra o seletor, apenas uma mudança real de comportamento quebraria. Esse desacoplamento é um tema recorrente em testes de Vue que se mantêm ao longo do tempo. ## Testando eventos de usuário e eventos emitidos Componentes interativos exigem verificações sobre o que acontece após um clique ou uma digitação. O Vue Test Utils retorna uma promise a partir do `trigger`, e aguardá-la esvazia a fila de reatividade do Vue, de modo que o DOM reflita a atualização. O utilitário `emitted` registra cada evento personalizado que o componente disparou, e é assim que os contratos entre pai e filho são verificados. ```typescript // SearchBar.spec.ts import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import SearchBar from '@/components/SearchBar.vue' describe('SearchBar', () => { it('emits search with the typed query', async () => { const wrapper = mount(SearchBar) // setValue writes to the input and fires an input event await wrapper.get('input').setValue('vitest') // awaiting trigger flushes the reactivity queue before assertions await wrapper.get('[data-test="submit"]').trigger('click') // emitted() returns a record of every event and its payloads const events = wrapper.emitted('search') expect(events).toHaveLength(1) // events[0] is the first emission, [0] its first argument expect(events![0][0]).toBe('vitest') }) }) ``` > **Sempre aguarde as atualizações do DOM** > > O Vue agrupa as atualizações reativas de forma assíncrona. Esquecer de usar `await` em uma chamada de `trigger` ou `setValue` é a causa mais comum de testes instáveis em Vue: as verificações rodam antes de o DOM re-renderizar, então leem uma saída desatualizada. Quando a mudança de estado acontece fora de um utilitário de evento, importe o `nextTick` de `vue` e use `await nextTick()` antes de verificar. ## Testando composables do Vue de forma isolada Composables que usam apenas APIs de reatividade (`ref`, `computed`, `watch`) não precisam de nenhum componente: são funções puras que retornam estado reativo, então chamá-las diretamente e verificar o `.value` já basta. Essa é a maneira mais rápida e mais focada de cobrir a lógica de negócio, e combina bem com os padrões apresentados em [composables avançados no Vue 3](/blog/vue-nuxt/advanced-vue-3-composables-reusable-patterns). ```typescript // useCart.spec.ts import { describe, it, expect } from 'vitest' import { useCart } from '@/composables/useCart' describe('useCart', () => { it('tracks total price as items change', () => { // composables using only ref/computed run without mounting const { items, total, addItem } = useCart() expect(total.value).toBe(0) addItem({ id: 1, price: 1500, qty: 2 }) // computed total recalculates synchronously on state change expect(total.value).toBe(3000) addItem({ id: 2, price: 500, qty: 1 }) expect(items.value).toHaveLength(2) expect(total.value).toBe(3500) }) }) ``` Composables que dependem de hooks de ciclo de vida como `onMounted` são a exceção: eles precisam rodar dentro de uma instância de componente. A solução comum é um pequeno componente auxiliar que chama o composable no `setup` e expõe o resultado, e então montar esse auxiliar com o Vue Test Utils. ## Mockando chamadas de API com vi.mock Requisições de rede reais deixam os testes lentos e não determinísticos. O Vitest substitui módulos com `vi.mock` e cria funções espiãs com `vi.fn`. O padrão abaixo mocka o módulo que encapsula o `fetch`, de modo que o composable sob teste receba dados controlados sem jamais tocar em um servidor. ```typescript // useProducts.spec.ts import { describe, it, expect, vi, beforeEach } from 'vitest' import { useProducts } from '@/composables/useProducts' import { getProducts } from '@/api/products' // replace the whole module with mocked exports vi.mock('@/api/products', () => ({ getProducts: vi.fn(), })) describe('useProducts', () => { beforeEach(() => { // reset call history between tests to avoid leakage vi.mocked(getProducts).mockReset() }) it('exposes fetched products and clears loading', async () => { // define what the mocked API returns for this test vi.mocked(getProducts).mockResolvedValue([ { id: 1, name: 'Keyboard' }, ]) const { products, loading, load } = useProducts() await load() expect(getProducts).toHaveBeenCalledOnce() expect(loading.value).toBe(false) expect(products.value[0].name).toBe('Keyboard') }) }) ``` Mockar na fronteira do módulo mantém a lógica interna do composable intacta e, ao mesmo tempo, dá a cada teste controle total sobre a resposta da API, inclusive os caminhos de erro. Usar `mockRejectedValue` no lugar permite que um único teste verifique se as falhas definem um estado de erro e param o spinner. ## Testando stores do Pinia em 2026 Stores concentram estado compartilhado entre componentes, e o pacote oficial `@pinia/testing` torna simples testá-los. O `createTestingPinia` transforma cada action em stub por padrão, então um teste de componente pode verificar que uma action foi despachada sem executar seus efeitos colaterais. O [guia de testes do Pinia](https://pinia.vuejs.org/cookbook/testing.html) recomenda essa abordagem para testes no nível de componente. ```typescript // CheckoutButton.spec.ts import { describe, it, expect, vi } from 'vitest' import { mount } from '@vue/test-utils' import { createTestingPinia } from '@pinia/testing' import CheckoutButton from '@/components/CheckoutButton.vue' import { useCartStore } from '@/stores/cart' describe('CheckoutButton', () => { it('dispatches checkout on click', async () => { const wrapper = mount(CheckoutButton, { global: { plugins: [ // vi.fn spies on actions instead of executing them createTestingPinia({ createSpy: vi.fn }), ], }, }) const store = useCartStore() await wrapper.get('[data-test="checkout"]').trigger('click') // the action is stubbed, so only the dispatch is asserted expect(store.checkout).toHaveBeenCalledOnce() }) }) ``` Para testar a lógica da própria store em vez da integração com o componente, passe `stubActions: false` para que as actions executem normalmente e depois verifique o estado resultante. Essa separação entre testes de componente com actions em stub e testes de store com actions reais mantém cada suíte focada em uma única responsabilidade. ## Rodando testes em modo watch e medindo cobertura O Vitest traz um modo watch que reexecuta apenas os testes afetados por um arquivo salvo, o que encurta o ciclo de feedback durante o desenvolvimento para uma fração de segundo. O relatório de cobertura já vem embutido por meio do provider `@vitest/coverage-v8` e se integra de forma limpa a um pipeline de integração contínua, onde um teste que falha ou uma regressão de cobertura pode bloquear um merge. ```typescript // vitest.config.ts (coverage section) export default defineConfig({ test: { coverage: { // v8 is the fastest provider and needs no instrumentation step provider: 'v8', reporter: ['text', 'html', 'lcov'], // fail CI when a threshold drops below the target thresholds: { statements: 80, branches: 75, functions: 80, }, // exclude generated and config files from the report exclude: ['**/*.config.ts', '**/types/**'], }, }, }) ``` Rodar `vitest` sozinho inicia o modo watch localmente, enquanto `vitest run --coverage` executa uma única passagem, adequada para a CI. Definir limiares explícitos transforma a cobertura de uma métrica de vaidade em um portão de qualidade obrigatório: um pull request que derruba a cobertura de branches abaixo de 75 por cento falha automaticamente, o que incentiva os colaboradores a testar os caminhos que adicionam, e não apenas o caminho feliz. ## Perguntas comuns de entrevista sobre testes em Vue e suas respostas Perguntas sobre testes aparecem na maioria das entrevistas de Vue sênior porque revelam como a pessoa candidata pensa em manutenibilidade. As respostas a seguir cobrem os conceitos que surgem com mais frequência, e o conjunto completo é treinado no [módulo de entrevista sobre testes em Vue](/technologies/vue-nuxt/interview-questions/vue-testing). **Quando usar `shallowMount` em vez de `mount`?** Recorra ao `shallowMount` quando um componente renderiza filhos pesados ou já testados e o teste só se importa com a lógica do próprio pai. Transformar os filhos em stubs isola a unidade e acelera a renderização, ao custo de não verificar a integração real entre os componentes. **Como as atualizações assíncronas são tratadas no Vue Test Utils?** O Vue aplica as mudanças reativas no próximo tick. Utilitários de evento como `trigger` e `setValue` retornam promises que resolvem depois que o DOM é atualizado, então aguardá-los já é suficiente. Para estado alterado fora desses utilitários, `await nextTick()` força a fila a esvaziar antes de as verificações rodarem. **Qual é a diferença entre `find` e `get`?** O `find` retorna um wrapper vazio quando nenhum elemento corresponde e nunca lança erro, o que combina com verificações como `expect(wrapper.find('.error').exists()).toBe(false)`. O `get` lança um erro imediatamente em um elemento ausente, tornando-o a melhor escolha quando o elemento deveria existir e sua ausência é uma falha genuína. **Por que preferir atributos `data-test` a seletores de classe?** Nomes de classe mudam junto com o estilo e a estrutura, então testes que os consultam quebram em edições cosméticas. Um gancho `data-test` dedicado expressa a intenção do teste de forma explícita e sobrevive às refatorações, reduzindo os falsos negativos. **Como testar um composable que depende de `onMounted`?** Hooks de ciclo de vida só disparam dentro de uma instância de componente, então o composable não pode ser chamado diretamente. A técnica padrão é um componente descartável que atua como "harness", invocando o composable no `setup` e expondo o valor de retorno, e então montá-lo com o Vue Test Utils e verificar por meio do wrapper. Isso mantém a fiação de reatividade e de ciclo de vida intacta e ainda assim isola a lógica sob teste. Mais perguntas conceituais e de código estão reunidas no guia de [perguntas essenciais de entrevista sobre Vue.js](/blog/vue-nuxt/essential-vuejs-interview-questions). ## Conclusão Uma configuração de testes em Vue confiável em 2026 se resume a algumas escolhas deliberadas: - Rode o Vitest com `environment: 'jsdom'` e o plugin do Vue para que os testes compartilhem o pipeline exato de transformação do Vite da aplicação. - Prefira `mount` a `shallowMount`, a menos que um componente filho seja pesado ou já esteja coberto em outro lugar. - Sempre use `await` em `trigger`, `setValue` e `nextTick` antes de verificar, para eliminar testes instáveis dependentes de temporização. - Teste composables que usam apenas APIs de reatividade chamando-os diretamente e verificando o `.value`. - Mocke na fronteira do módulo com `vi.mock` para que o comportamento de rede seja determinístico e os caminhos de erro sejam testáveis. - Use `createTestingPinia({ createSpy: vi.fn })` para testes de componente e `stubActions: false` ao exercitar a lógica da store. - Consulte os elementos por atributos `data-test` para que os testes quebrem apenas em mudanças reais de comportamento, nunca por estilo. Domine esses padrões e testar deixa de ser um fardo para virar uma ferramenta de design, capturando regressões antes que cheguem à produção. Explore a [trilha completa de aprendizado de Vue e Nuxt](/technologies/vue-nuxt) para continuar desenvolvendo habilidades prontas para entrevista. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pt/blog/vue-nuxt/vue-3-testing-vitest-vue-test-utils