Tests in a Vue project usually come in three layers. Pure logic such as composables and stores is checked with Vitest, components with Vitest plus Vue Test Utils, and real user flows with an end-to-end tool such as Playwright. In Nuxt projects, @nuxt/test-utils boots a Nuxt environment inside your tests.
Setting up Vitest
If you selected Vitest in create-vue, everything is already configured. To add it by hand, install these packages:
npm install -D vitest @vue/test-utils jsdom
vitest.config.ts extends the Vite config and selects the jsdom environment, which emulates browser APIs:
import { fileURLToPath } from "node:url";
import { mergeConfig, defineConfig, configDefaults } from "vitest/config";
import viteConfig from "./vite.config";
export default mergeConfig(
viteConfig,
defineConfig({
test: {
environment: "jsdom",
exclude: [...configDefaults.exclude, "e2e/**"],
root: fileURLToPath(new URL("./", import.meta.url)),
},
}),
);
npm run test:unit (or npx vitest) runs tests in watch mode, re-running only the affected tests each time you save.
Testing logic and stores
Call a composable like any function and assert on the result. For Pinia stores, activate a fresh Pinia before each test so tests do not leak state into each other.
import { describe, it, expect, beforeEach } from "vitest";
import { setActivePinia, createPinia } from "pinia";
import { useCounter } from "@/composables/useCounter";
import { useCartStore } from "@/stores/cart";
describe("useCounter", () => {
it("increments and doubles", () => {
const { count, double, increment } = useCounter(1);
increment();
expect(count.value).toBe(2);
expect(double.value).toBe(4);
});
});
describe("cart store", () => {
beforeEach(() => setActivePinia(createPinia()));
it("increases quantity for the same item", () => {
const cart = useCartStore();
cart.add({ id: 1, name: "Book", price: 100 });
cart.add({ id: 1, name: "Book", price: 100 });
expect(cart.items[0].qty).toBe(2);
expect(cart.total).toBe(200);
});
});
Testing components
mount from Vue Test Utils renders a component and returns a wrapper. Pass props, find elements, trigger events, then assert on the rendered text or emitted events. DOM updates are asynchronous, so await calls to trigger and setValue.
import { describe, it, expect } from "vitest";
import { mount } from "@vue/test-utils";
import TodoItem from "@/components/TodoItem.vue";
describe("TodoItem", () => {
it("renders the text and emits remove", async () => {
const wrapper = mount(TodoItem, { props: { text: "Groceries", done: false } });
expect(wrapper.text()).toContain("Groceries");
await wrapper.find("button").trigger("click");
expect(wrapper.emitted("remove")?.[0]).toEqual(["user"]);
});
});
0 Kommentare
Anmelden · Melde dich an, um einen Kommentar zu schreiben.