@repo/qa
Testing utilities, fixtures, mocks, and custom matchers for Vitest and Testing Library. Deterministic test data, API mocks, database mocks. Install as dev dependency only.
Quick Start
Add testing utilities in 2 minutes:
pnpm add -D @repo/qaFixtures, mocks, custom matchers included. Dev dependency only. Skip to Quick Start →
Why @repo/qa?
Every test file creates its own fixtures. Test data inconsistent across specs. Mocking requires boilerplate. API mocks duplicated. Database mocks need setup. Custom matchers written multiple times. Test utilities scattered.
@repo/qa solves this with shared testing utilities, fixtures, and mocks for the entire monorepo.
Production-ready with Vitest integration, Testing Library support, deterministic fixtures, and custom matchers.
Use cases
- Test fixtures — Consistent user, post, product data across all tests
- API mocks — Mock fetch calls with response history
- Database mocks — Mock Prisma client for unit tests
- Custom matchers —
toMatchStructure,toBeWithinRangefor better assertions - Test helpers — Render with providers, fake timers, mock storage
How it works
@repo/qa exports fixtures, mocks, and utilities:
import { fixtures, mockPrisma, renderWithProviders } from "@repo/qa";
// Fixtures - consistent test data
const user = fixtures.user();
const admin = fixtures.user({ role: "admin" });
// Mocks - database operations
const prisma = mockPrisma();
prisma.user.findMany.mockResolvedValue([user]);
// Utilities - render with context
const { getByText } = renderWithProviders(<MyComponent />, {
user,
theme: "dark",
});Uses Vitest mocks, Testing Library, and custom utilities for testing.
Key features
Test fixtures — Deterministic data with override support
API/Service mocks — Mock fetch, services, databases
Custom matchers — Better assertions for structure, ranges
Render utilities — Testing Library with providers
Test helpers — Fake timers, mock date, mock storage
Vitest setup — Auto-configures testing environment
Quick Start
1. Install the package
pnpm add -D @repo/qa2. Use fixtures for consistent test data
import { fixtures } from "@repo/qa/fixtures";
import { describe, it, expect } from "vitest";
describe("User", () => {
it("creates a user with default values", () => {
const user = fixtures.user();
expect(user).toMatchObject({
id: expect.any(String),
email: expect.stringContaining("@"),
name: expect.any(String)
});
});
it("creates an admin user", () => {
const admin = fixtures.user({ role: "admin" });
expect(admin.role).toBe("admin");
});
});3. Mock database operations
import { mockPrisma } from "@repo/qa/mocks";
import { fixtures } from "@repo/qa/fixtures";
import { UserService } from "#/services/user";
import { describe, it, expect } from "vitest";
describe("UserService", () => {
it("fetches users from database", async () => {
const prisma = mockPrisma();
const users = [fixtures.user(), fixtures.user()];
prisma.user.findMany.mockResolvedValue(users);
const service = new UserService(prisma);
const result = await service.getAllUsers();
expect(result).toHaveLength(2);
expect(prisma.user.findMany).toHaveBeenCalled();
});
});4. Add Vitest setup
import "@repo/qa/setup";
// Automatically sets up:
// - Testing Library
// - Custom matchers
// - Browser API mocksimport { defineConfig } from "vitest/config";
export default defineConfig({
test: {
setupFiles: ["./vitest.setup.ts"]
}
});That's it! You now have fixtures, mocks, and utilities for consistent testing across your app.
Use custom matchers
Better assertions with custom matchers:
import "@repo/qa/matchers";
expect(object).toMatchStructure({
id: String,
count: Number,
items: Array
});Technical Details
For Developers: Technical implementation details
Overview
| Property | Value |
|---|---|
| Location | packages/qa |
| Purpose | Testing utilities, fixtures, mocks |
| Frameworks | Vitest, Testing Library |
Export Paths
| Path | Description |
|---|---|
@repo/qa | Main exports |
@repo/qa/fixtures | Test fixtures |
@repo/qa/mocks | Mock utilities |
@repo/qa/matchers | Custom matchers |
Test Fixtures
Deterministic Testing
Fixtures provide consistent test data. Override specific fields while keeping sensible defaults.
User Fixtures
import { fixtures } from "@repo/qa/fixtures";
// highlight-next-line
const user = fixtures.user();
// { id: "user_xxx", name: "Test User", email: "test@example.com", ... }
// highlight-start
const admin = fixtures.user({ role: "admin" });
const users = fixtures.users(5); // Array of 5 users
// highlight-endPost Fixtures
import { fixtures } from "@repo/qa/fixtures";
const post = fixtures.post();
const postWithAuthor = fixtures.post({ author: fixtures.user() });
const posts = fixtures.posts(10);Custom Fixtures
import { createFixture } from "@repo/qa/fixtures";
const productFixture = createFixture({
id: () => `prod_${Math.random().toString(36).slice(2)}`,
name: "Test Product",
price: 99.99,
inStock: true
});
const product = productFixture();
const customProduct = productFixture({ price: 149.99 });Mocks
API Mocks
import { mockApi } from "@repo/qa/mocks";
const api = mockApi();
api.get("/users").reply(200, [{ id: "1", name: "John" }]);
api.post("/users").reply(201, { id: "2", name: "Jane" });
// Use in tests
await fetch("/users");
expect(api.history.get).toHaveLength(1);Service Mocks
import { mockService } from "@repo/qa/mocks";
const emailService = mockService({
send: vi.fn().mockResolvedValue({ success: true }),
verify: vi.fn().mockResolvedValue(true)
});
// Use and verify
await emailService.send({ to: "test@example.com" });
expect(emailService.send).toHaveBeenCalled();Database Mocks
Isolation
Mock the database in unit tests. Use a real test database for integration tests.
import { mockPrisma } from "@repo/qa/mocks";
// highlight-next-line
const prisma = mockPrisma();
prisma.user.findMany.mockResolvedValue([fixtures.user()]);
// Use in tests
const users = await prisma.user.findMany();
expect(users).toHaveLength(1);Test Utilities
Render with Providers
import { renderWithProviders } from "@repo/qa";
const { getByText, getByRole } = renderWithProviders(<MyComponent />, {
user: fixtures.user(),
theme: "dark",
});Wait For
import { waitForElement, waitForText } from "@repo/qa";
await waitForElement("[data-testid='result']");
await waitForText("Success!");Act Async
import { actAsync } from "@repo/qa";
await actAsync(async () => {
fireEvent.click(button);
await waitFor(() => expect(result).toBeVisible());
});Custom Matchers
toHaveBeenCalledWithMatch
import "@repo/qa/matchers";
expect(mockFn).toHaveBeenCalledWithMatch({
id: expect.any(String),
name: "John"
});toBeWithinRange
expect(value).toBeWithinRange(1, 10);toMatchStructure
expect(object).toMatchStructure({
id: String,
count: Number,
items: Array
});Test Helpers
Fake Timers
import { fakeTimers } from "@repo/qa";
fakeTimers.install();
// ... test code
fakeTimers.tick(1000);
fakeTimers.uninstall();Mock Date
import { mockDate } from "@repo/qa";
mockDate.set("2024-01-15");
// Date.now() returns timestamp for 2024-01-15
mockDate.reset();Mock Storage
import { mockLocalStorage, mockSessionStorage } from "@repo/qa";
const localStorage = mockLocalStorage();
localStorage.setItem("key", "value");
expect(localStorage.getItem("key")).toBe("value");Setup Files
Vitest Setup
// vitest.setup.ts
import "@repo/qa/setup";
// Automatically:
// - Sets up testing-library
// - Adds custom matchers
// - Mocks common browser APIsEnvironment
import { testEnv } from "@repo/qa";
testEnv.set({
DATABASE_URL: "test://localhost",
API_KEY: "test-key"
});
// Clean up
testEnv.reset();Related Packages
- @repo/types - Type definitions
- @repo/utils - Utility functions