# Testing in Vue 3 nel 2026: Vitest, Vue Test Utils e domande da colloquio > Guida pratica al testing in Vue nel 2026: configurazione di Vitest, montaggio dei componenti con Vue Test Utils, test di composable e store Pinia, mock delle API, misurazione della copertura e le domande da colloquio poste dai team di selezione. - Published: 2026-06-21 - Updated: 2026-07-06 - Author: SharpSkill - Tags: vue, testing, vitest, vue-test-utils, interview - Reading time: 10 min --- Il testing in Vue distingue i refactoring sicuri dalle scommesse fragili, e nel 2026 la toolchain si è consolidata attorno a due librerie: [Vitest](https://vitest.dev/guide/) come test runner e [Vue Test Utils](https://test-utils.vuejs.org/) per il montaggio dei componenti. Questa guida illustra la configurazione dello stack, il test di componenti e composable, il mock delle dipendenze e le risposte alle domande da colloquio sul testing in Vue che i team di selezione pongono davvero. > **Lo stack moderno per il testing in Vue nel 2026** > > La raccomandazione predefinita della [documentazione di Vue](https://vuejs.org/guide/scaling-up/testing.html) prevede Vitest per i test unitari e dei componenti, Vue Test Utils come API di montaggio a basso livello e Playwright o Cypress per la copertura end-to-end. Vitest riutilizza la stessa configurazione Vite dell'applicazione, quindi i test passano per la pipeline di trasformazione identica, con un hot reload quasi istantaneo. ## Configurare Vitest per un progetto Vue 3 Vitest condivide la pipeline di Vite, il che significa che un unico file di configurazione governa sia il dev server sia il test runner. L'opzione `environment` deve essere impostata su `jsdom` o `happy-dom`, così i test dei componenti dispongono di un DOM su cui renderizzare. Il flag `globals: true` espone `describe`, `it` ed `expect` senza doverli importare in ogni file. ```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)), }, }, }) ``` Con Vitest 3 la configurazione qui sopra funziona da subito dopo l'installazione di `vitest`, `@vue/test-utils`, `@vitejs/plugin-vue` e `jsdom`. Poiché l'alias rispecchia quello dell'applicazione, import come `@/composables/useCart` si risolvono in modo identico nei test e in produzione. ## Montare i componenti con Vue Test Utils Vue Test Utils espone due punti di ingresso: `mount`, che renderizza l'intero albero del componente, e `shallowMount`, che sostituisce i componenti figli con degli stub. Per la maggior parte dei test unitari `mount` è la scelta preferibile, perché mette alla prova il comportamento di rendering reale. Il `wrapper` restituito offre helper di interrogazione come `find`, `get` e `text` per verificare l'output renderizzato. ```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') }) }) ``` Raggiungere gli elementi tramite attributi `data-test` anziché tramite classi CSS mantiene i test resilienti: una modifica di stile a `.price--sale` non romperà il selettore, solo un cambiamento reale di comportamento lo farà. Questo disaccoppiamento è un tema ricorrente in un testing Vue manutenibile. ## Testare eventi utente ed eventi emessi I componenti interattivi richiedono asserzioni su ciò che accade dopo un clic o un input. Vue Test Utils restituisce una promise da `trigger`, e attenderla svuota la coda di reattività di Vue affinché il DOM rifletta l'aggiornamento. L'helper `emitted` registra ogni evento personalizzato emesso da un componente, ed è così che si verificano i contratti tra genitore e figlio. ```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') }) }) ``` > **Attendere sempre gli aggiornamenti del DOM** > > Vue applica gli aggiornamenti reattivi in modo asincrono. Dimenticare di usare `await` su una chiamata a `trigger` o `setValue` è la causa più comune di test Vue instabili: le asserzioni vengono eseguite prima che il DOM venga renderizzato di nuovo, quindi leggono un output obsoleto. Quando un cambiamento di stato avviene al di fuori di un helper di evento, occorre importare `nextTick` da `vue` e usare `await nextTick()` prima di verificare. ## Testare i composable Vue in isolamento I composable che usano solo le API di reattività (`ref`, `computed`, `watch`) non hanno bisogno di alcun componente: sono semplici funzioni che restituiscono uno stato reattivo, quindi chiamarle direttamente e verificare `.value` è sufficiente. È il modo più rapido e mirato per coprire la logica di business, e si sposa bene con i pattern descritti nei [composable avanzati in 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) }) }) ``` I composable che dipendono da hook del ciclo di vita come `onMounted` sono l'eccezione: devono essere eseguiti all'interno di un'istanza di componente. La soluzione comune è un minuscolo componente di supporto che chiama il composable in `setup` ed espone il risultato, per poi montare quel componente con Vue Test Utils. ## Mock delle chiamate API con vi.mock Le richieste di rete reali rendono i test lenti e non deterministici. Vitest sostituisce i moduli con `vi.mock` e crea funzioni spia con `vi.fn`. Il pattern seguente esegue il mock del modulo che avvolge `fetch`, così il composable sotto test riceve dati controllati senza mai raggiungere un server. ```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') }) }) ``` Eseguire il mock al confine del modulo lascia intatta la logica interna del composable, dando al contempo a ogni test il pieno controllo sulla risposta dell'API, inclusi i percorsi di errore. Chiamare invece `mockRejectedValue` permette a un singolo test di verificare che i fallimenti impostino uno stato di errore e fermino lo spinner. ## Testare gli store Pinia nel 2026 Gli store contengono stato condiviso tra più componenti, e il pacchetto ufficiale `@pinia/testing` ne semplifica il test. `createTestingPinia` sostituisce per impostazione predefinita ogni action con uno stub, così un test di componente può verificare che un'action sia stata dispatchata senza eseguirne gli effetti collaterali. La [guida al testing di Pinia](https://pinia.vuejs.org/cookbook/testing.html) raccomanda questo approccio per i test a livello di 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() }) }) ``` Per testare la logica dello store in sé, anziché l'integrazione con il componente, si passa `stubActions: false` così le action vengono eseguite normalmente, per poi verificare lo stato risultante. Questa separazione tra test di componente con action sostituite e test di store con action reali mantiene ogni suite concentrata su una singola responsabilità. ## Eseguire i test in watch mode e misurare la copertura Vitest include una watch mode che riesegue solo i test interessati da un file salvato, riducendo il ciclo di feedback durante lo sviluppo a una frazione di secondo. Il report di copertura è integrato tramite il provider `@vitest/coverage-v8` e si inserisce senza attriti in una pipeline di integrazione continua, dove un test fallito o una regressione della copertura può bloccare un 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/**'], }, }, }) ``` Eseguire `vitest` da solo avvia la watch mode in locale, mentre `vitest run --coverage` esegue un unico passaggio adatto alla CI. Impostare soglie esplicite trasforma la copertura da metrica di facciata in un cancello di qualità imposto: una pull request che porta la copertura dei rami sotto il 75 percento fallisce automaticamente, spingendo chi contribuisce a testare i percorsi che aggiunge anziché solo il caso ideale. ## Domande e risposte frequenti da colloquio sul testing in Vue Le domande sul testing emergono nella maggior parte dei colloqui Vue senior perché rivelano come un candidato ragiona sulla manutenibilità. Le risposte che seguono coprono i concetti più ricorrenti, mentre l'insieme completo viene approfondito nel [modulo di domande da colloquio sul testing in Vue](/technologies/vue-nuxt/interview-questions/vue-testing). **Quando conviene usare `shallowMount` invece di `mount`?** Si ricorre a `shallowMount` quando un componente renderizza figli pesanti o già testati e il test riguarda solo la logica del genitore. Sostituire i figli con stub isola l'unità e velocizza il rendering, al prezzo di non verificare l'integrazione reale tra i componenti. **Come si gestiscono gli aggiornamenti asincroni in Vue Test Utils?** Vue applica i cambiamenti reattivi al tick successivo. Gli helper di evento come `trigger` e `setValue` restituiscono promise che si risolvono dopo l'aggiornamento del DOM, quindi attenderle è sufficiente. Per lo stato modificato al di fuori di questi helper, `await nextTick()` forza lo svuotamento della coda prima che vengano eseguite le asserzioni. **Qual è la differenza tra `find` e `get`?** `find` restituisce un wrapper vuoto quando nessun elemento corrisponde e non solleva mai un'eccezione, il che si adatta ad asserzioni come `expect(wrapper.find('.error').exists()).toBe(false)`. `get` solleva immediatamente un'eccezione su un elemento mancante, rendendolo la scelta migliore quando ci si aspetta che l'elemento esista e la sua assenza è un fallimento autentico. **Perché preferire gli attributi `data-test` ai selettori di classe?** I nomi delle classi cambiano con lo stile e la struttura, quindi i test che li interrogano si rompono con modifiche cosmetiche. Un hook `data-test` dedicato esprime esplicitamente l'intento del test e sopravvive ai refactoring, riducendo i falsi negativi. **Come si testa un composable che dipende da `onMounted`?** Gli hook del ciclo di vita scattano solo all'interno di un'istanza di componente, quindi il composable non può essere chiamato direttamente. La tecnica standard prevede un componente usa e getta che invoca il composable in `setup` ed espone il suo valore di ritorno, per poi montare quel componente con Vue Test Utils e verificare tramite il wrapper. In questo modo la reattività e il cablaggio del ciclo di vita restano intatti, isolando comunque la logica sotto test. Altre domande concettuali e di programmazione sono raccolte nella guida alle [domande essenziali da colloquio su Vue.js](/blog/vue-nuxt/essential-vuejs-interview-questions). ## Conclusione Una configurazione di testing Vue affidabile nel 2026 si riduce a poche scelte deliberate: - Eseguire Vitest con `environment: 'jsdom'` e il plugin Vue, così i test condividono la pipeline di trasformazione Vite esatta dell'applicazione. - Preferire `mount` a `shallowMount`, a meno che un componente figlio sia pesante o già coperto altrove. - Usare sempre `await` su `trigger`, `setValue` e `nextTick` prima di verificare, per eliminare i test instabili e dipendenti dal timing. - Testare i composable che usano solo le API di reattività chiamandoli direttamente e verificando `.value`. - Eseguire il mock al confine del modulo con `vi.mock`, così il comportamento di rete è deterministico e i percorsi di errore sono testabili. - Usare `createTestingPinia({ createSpy: vi.fn })` per i test di componente e `stubActions: false` quando si mette alla prova la logica dello store. - Interrogare gli elementi tramite attributi `data-test`, così i test si rompono solo su cambiamenti reali di comportamento, mai sullo stile. Padroneggiare questi pattern trasforma il testing in uno strumento di progettazione anziché in una scocciatura, intercettando le regressioni prima che raggiungano la produzione. Si esplori il [percorso di apprendimento completo su Vue e Nuxt](/technologies/vue-nuxt) per continuare a costruire competenze pronte per il colloquio. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/vue-nuxt/vue-3-testing-vitest-vue-test-utils