skills/doanchienthangdev/omgkit/Testing with Vitest

Testing with Vitest

SKILL.md

Testing with Vitest

Quick Start

// vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,
    environment: "jsdom",
    setupFiles: ["./tests/setup.ts"],
    coverage: {
      provider: "v8",
      reporter: ["text", "html"],
      thresholds: { global: { branches: 80, functions: 80, lines: 80 } },
    },
  },
});

// tests/setup.ts
import { afterEach, vi } from "vitest";
import { cleanup } from "@testing-library/vue";
import "@testing-library/jest-dom/vitest";

afterEach(() => { vi.clearAllMocks(); cleanup(); });

Features

Feature Description Reference
Unit Testing Fast isolated tests with describe/it/expect Vitest API
Mocking Module, function, and timer mocking with vi Mocking Guide
Snapshot Testing Component and data structure snapshots Snapshot Testing
Component Testing Vue/React component testing with Testing Library Vue Test Utils
Coverage Reports V8/Istanbul coverage with thresholds Coverage
Parallel Execution Multi-threaded test runner Test Runner

Common Patterns

Unit Testing with Parametrization

import { describe, it, expect, test } from "vitest";

describe("String Utils", () => {
  test.each([
    ["hello", 3, "hel..."],
    ["hi", 10, "hi"],
    ["test", 4, "test"],
  ])('truncate("%s", %d) returns "%s"', (str, len, expected) => {
    expect(truncate(str, len)).toBe(expected);
  });
});

Mocking Modules and Services

import { describe, it, expect, vi, beforeEach, Mock } from "vitest";
import { UserService } from "@/services/user";
import { apiClient } from "@/lib/api";

vi.mock("@/lib/api", () => ({
  apiClient: { get: vi.fn(), post: vi.fn() },
}));

describe("UserService", () => {
  beforeEach(() => vi.clearAllMocks());

  it("fetches user from API", async () => {
    const mockUser = { id: "1", name: "John" };
    (apiClient.get as Mock).mockResolvedValue({ data: mockUser });

    const user = await new UserService().getUser("1");

    expect(apiClient.get).toHaveBeenCalledWith("/users/1");
    expect(user).toEqual(mockUser);
  });
});

Vue Component Testing

import { describe, it, expect, vi } from "vitest";
import { mount } from "@vue/test-utils";
import Button from "@/components/Button.vue";

describe("Button", () => {
  it("emits click event", async () => {
    const wrapper = mount(Button, { slots: { default: "Click" } });
    await wrapper.trigger("click");
    expect(wrapper.emitted("click")).toHaveLength(1);
  });

  it("is disabled when loading", () => {
    const wrapper = mount(Button, { props: { loading: true } });
    expect(wrapper.attributes("disabled")).toBeDefined();
  });
});

Testing Async Operations with Timers

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";

describe("Debounce", () => {
  beforeEach(() => vi.useFakeTimers());
  afterEach(() => vi.useRealTimers());

  it("delays function execution", () => {
    const fn = vi.fn();
    const debouncedFn = debounce(fn, 100);

    debouncedFn();
    expect(fn).not.toHaveBeenCalled();

    vi.advanceTimersByTime(100);
    expect(fn).toHaveBeenCalledTimes(1);
  });
});

Best Practices

Do Avoid
Use descriptive test names explaining behavior Testing implementation details
Test behavior, not internal state Sharing state between tests
Use test.each for multiple similar cases Using arbitrary timeouts
Mock external dependencies Over-mocking internal modules
Keep tests focused and isolated Duplicating test coverage
Write tests alongside code Ignoring flaky tests

References

Weekly Installs
0
GitHub Stars
3
First Seen
Jan 1, 1970