Write effective tests for Vue components using Vue Test Utils and testing best practices.
# Vue Testing with Vue Test Utils for Google Antigravity
Master Vue.js component testing using Vue Test Utils in your Google Antigravity projects. This guide covers unit testing, component mounting, event handling, and integration testing patterns.
## Testing Setup Configuration
Configure Vitest with Vue Test Utils:
```typescript
// vitest.config.ts
import { defineConfig } from "vitest/config";
import vue from "@vitejs/plugin-vue";
import { fileURLToPath } from "url";
export default defineConfig({
plugins: [vue()],
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./test/setup.ts"],
include: ["**/*.{test,spec}.{js,ts,vue}"],
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
exclude: ["node_modules/", "test/"],
},
},
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
```
```typescript
// test/setup.ts
import { config } from "@vue/test-utils";
import { createPinia, setActivePinia } from "pinia";
// Global test setup
beforeEach(() => {
setActivePinia(createPinia());
});
// Global component stubs
config.global.stubs = {
RouterLink: {
template: "<a><slot /></a>",
},
RouterView: true,
};
// Global mocks
config.global.mocks = {
$t: (key: string) => key,
$route: {
path: "/",
params: {},
query: {},
},
$router: {
push: vi.fn(),
replace: vi.fn(),
},
};
```
## Component Unit Testing
Test Vue components thoroughly:
```typescript
// src/components/Button/__tests__/Button.spec.ts
import { describe, it, expect, vi } from "vitest";
import { mount } from "@vue/test-utils";
import Button from "../Button.vue";
describe("Button", () => {
it("renders with default props", () => {
const wrapper = mount(Button, {
slots: {
default: "Click me",
},
});
expect(wrapper.text()).toBe("Click me");
expect(wrapper.classes()).toContain("btn-primary");
});
it("applies variant classes correctly", async () => {
const wrapper = mount(Button, {
props: {
variant: "secondary",
},
slots: {
default: "Secondary",
},
});
expect(wrapper.classes()).toContain("btn-secondary");
await wrapper.setProps({ variant: "outline" });
expect(wrapper.classes()).toContain("btn-outline");
});
it("emits click event when clicked", async () => {
const wrapper = mount(Button, {
slots: {
default: "Click me",
},
});
await wrapper.trigger("click");
expect(wrapper.emitted()).toHaveProperty("click");
expect(wrapper.emitted("click")).toHaveLength(1);
});
it("does not emit click when disabled", async () => {
const wrapper = mount(Button, {
props: {
disabled: true,
},
slots: {
default: "Disabled",
},
});
await wrapper.trigger("click");
expect(wrapper.emitted("click")).toBeUndefined();
});
it("shows loading state correctly", async () => {
const wrapper = mount(Button, {
props: {
loading: true,
},
slots: {
default: "Loading",
},
});
expect(wrapper.find(".spinner").exists()).toBe(true);
expect(wrapper.attributes("disabled")).toBeDefined();
});
it("renders as link when href is provided", () => {
const wrapper = mount(Button, {
props: {
href: "/dashboard",
},
slots: {
default: "Go to Dashboard",
},
});
expect(wrapper.element.tagName).toBe("A");
expect(wrapper.attributes("href")).toBe("/dashboard");
});
});
```
## Form Component Testing
Test form interactions:
```typescript
// src/components/LoginForm/__tests__/LoginForm.spec.ts
import { describe, it, expect, vi, beforeEach } from "vitest";
import { mount, flushPromises } from "@vue/test-utils";
import { createPinia, setActivePinia } from "pinia";
import LoginForm from "../LoginForm.vue";
import { useAuthStore } from "@/stores/auth";
describe("LoginForm", () => {
beforeEach(() => {
setActivePinia(createPinia());
});
it("renders all form fields", () => {
const wrapper = mount(LoginForm);
expect(wrapper.find("input[type=\"email\"]").exists()).toBe(true);
expect(wrapper.find("input[type=\"password\"]").exists()).toBe(true);
expect(wrapper.find("button[type=\"submit\"]").exists()).toBe(true);
});
it("validates email format", async () => {
const wrapper = mount(LoginForm);
const emailInput = wrapper.find("input[type=\"email\"]");
await emailInput.setValue("invalid-email");
await wrapper.find("form").trigger("submit");
await flushPromises();
expect(wrapper.text()).toContain("Please enter a valid email");
});
it("validates required fields", async () => {
const wrapper = mount(LoginForm);
await wrapper.find("form").trigger("submit");
await flushPromises();
expect(wrapper.text()).toContain("Email is required");
expect(wrapper.text()).toContain("Password is required");
});
it("submits form with valid data", async () => {
const authStore = useAuthStore();
const loginSpy = vi.spyOn(authStore, "login").mockResolvedValue(undefined);
const wrapper = mount(LoginForm);
await wrapper.find("input[type=\"email\"]").setValue("test@example.com");
await wrapper.find("input[type=\"password\"]").setValue("password123");
await wrapper.find("form").trigger("submit");
await flushPromises();
expect(loginSpy).toHaveBeenCalledWith({
email: "test@example.com",
password: "password123",
});
});
it("displays error message on failed login", async () => {
const authStore = useAuthStore();
vi.spyOn(authStore, "login").mockRejectedValue(new Error("Invalid credentials"));
const wrapper = mount(LoginForm);
await wrapper.find("input[type=\"email\"]").setValue("test@example.com");
await wrapper.find("input[type=\"password\"]").setValue("wrongpassword");
await wrapper.find("form").trigger("submit");
await flushPromises();
expect(wrapper.text()).toContain("Invalid credentials");
});
it("disables submit button while loading", async () => {
const authStore = useAuthStore();
vi.spyOn(authStore, "login").mockImplementation(
() => new Promise((resolve) => setTimeout(resolve, 1000))
);
const wrapper = mount(LoginForm);
await wrapper.find("input[type=\"email\"]").setValue("test@example.com");
await wrapper.find("input[type=\"password\"]").setValue("password123");
await wrapper.find("form").trigger("submit");
expect(wrapper.find("button[type=\"submit\"]").attributes("disabled")).toBeDefined();
});
});
```
## Composable Testing
Test Vue composables:
```typescript
// src/composables/__tests__/useCounter.spec.ts
import { describe, it, expect } from "vitest";
import { useCounter } from "../useCounter";
describe("useCounter", () => {
it("initializes with default value", () => {
const { count } = useCounter();
expect(count.value).toBe(0);
});
it("initializes with custom value", () => {
const { count } = useCounter(10);
expect(count.value).toBe(10);
});
it("increments count", () => {
const { count, increment } = useCounter();
increment();
expect(count.value).toBe(1);
increment();
expect(count.value).toBe(2);
});
it("decrements count", () => {
const { count, decrement } = useCounter(5);
decrement();
expect(count.value).toBe(4);
});
it("resets to initial value", () => {
const { count, increment, reset } = useCounter(5);
increment();
increment();
expect(count.value).toBe(7);
reset();
expect(count.value).toBe(5);
});
});
// src/composables/__tests__/useFetch.spec.ts
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { useFetch } from "../useFetch";
import { flushPromises } from "@vue/test-utils";
describe("useFetch", () => {
beforeEach(() => {
vi.stubGlobal("fetch", vi.fn());
});
afterEach(() => {
vi.unstubAllGlobals();
});
it("fetches data successfully", async () => {
const mockData = { id: 1, name: "Test" };
vi.mocked(fetch).mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(mockData),
} as Response);
const { data, error, isLoading } = useFetch("/api/test");
expect(isLoading.value).toBe(true);
await flushPromises();
expect(isLoading.value).toBe(false);
expect(data.value).toEqual(mockData);
expect(error.value).toBeNull();
});
it("handles fetch error", async () => {
vi.mocked(fetch).mockRejectedValueOnce(new Error("Network error"));
const { data, error, isLoading } = useFetch("/api/test");
await flushPromises();
expect(isLoading.value).toBe(false);
expect(data.value).toBeNull();
expect(error.value?.message).toBe("Network error");
});
});
```
## Pinia Store Testing
Test Pinia stores:
```typescript
// src/stores/__tests__/auth.spec.ts
import { describe, it, expect, vi, beforeEach } from "vitest";
import { setActivePinia, createPinia } from "pinia";
import { useAuthStore } from "../auth";
describe("AuthStore", () => {
beforeEach(() => {
setActivePinia(createPinia());
});
it("initializes with null user", () => {
const store = useAuthStore();
expect(store.user).toBeNull();
expect(store.isAuthenticated).toBe(false);
});
it("logs in user successfully", async () => {
const store = useAuthStore();
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({
user: { id: 1, email: "test@example.com" },
token: "mock-token",
}),
}));
await store.login({ email: "test@example.com", password: "password" });
expect(store.user).toEqual({ id: 1, email: "test@example.com" });
expect(store.isAuthenticated).toBe(true);
});
it("logs out user", async () => {
const store = useAuthStore();
store.user = { id: 1, email: "test@example.com" };
await store.logout();
expect(store.user).toBeNull();
expect(store.isAuthenticated).toBe(false);
});
});
```
Google Antigravity generates comprehensive Vue.js test suites with proper component mounting, event simulation, and store testing for reliable applications.This Vue.js prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their vue.js implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For Vue.js projects, consider mentioning your framework version, coding style, and any specific libraries you're using.