git:20260307.47a5dbc to git:20260326.db33dbc

7 added, 484 removed. Audit A to A.

---
name: javascript-testing-patterns
description: Implement comprehensive testing strategies using Jest, Vitest, and Testing Library for unit tests, integration tests, and end-to-end testing with mocking, fixtures, and test-driven development. Use when writing JavaScript/TypeScript tests, setting up test infrastructure, or implementing TDD/BDD workflows.
---
# JavaScript Testing Patterns
Comprehensive guide for implementing robust testing strategies in JavaScript/TypeScript applications using modern testing frameworks and best practices.
## When to Use This Skill
- Setting up test infrastructure for new projects
- Writing unit tests for functions and classes
- Creating integration tests for APIs and services
- Implementing end-to-end tests for user flows
- Mocking external dependencies and APIs
- Testing React, Vue, or other frontend components
- Implementing test-driven development (TDD)
- Setting up continuous testing in CI/CD pipelines
## Testing Frameworks
### Jest - Full-Featured Testing Framework
**Setup:**
```typescript
// jest.config.ts
import type { Config } from "jest";
const config: Config = {
preset: "ts-jest",
testEnvironment: "node",
roots: ["<rootDir>/src"],
testMatch: ["**/__tests__/**/*.ts", "**/?(*.)+(spec|test).ts"],
collectCoverageFrom: [
"src/**/*.ts",
"!src/**/*.d.ts",
"!src/**/*.interface.ts",
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
setupFilesAfterEnv: ["<rootDir>/src/test/setup.ts"],
};
export default config;
```
### Vitest - Fast, Vite-Native Testing
**Setup:**
```typescript
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
environment: "node",
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
exclude: ["**/*.d.ts", "**/*.config.ts", "**/dist/**"],
},
setupFiles: ["./src/test/setup.ts"],
},
});
```
## Unit Testing Patterns
### Pattern 1: Testing Pure Functions
```typescript
// utils/calculator.ts
export function add(a: number, b: number): number {
return a + b;
}
export function divide(a: number, b: number): number {
if (b === 0) {
throw new Error("Division by zero");
}
return a / b;
}
// utils/calculator.test.ts
import { describe, it, expect } from "vitest";
import { add, divide } from "./calculator";
describe("Calculator", () => {
describe("add", () => {
it("should add two positive numbers", () => {
expect(add(2, 3)).toBe(5);
});
it("should add negative numbers", () => {
expect(add(-2, -3)).toBe(-5);
});
it("should handle zero", () => {
expect(add(0, 5)).toBe(5);
expect(add(5, 0)).toBe(5);
});
});
describe("divide", () => {
it("should divide two numbers", () => {
expect(divide(10, 2)).toBe(5);
});
it("should handle decimal results", () => {
expect(divide(5, 2)).toBe(2.5);
});
it("should throw error when dividing by zero", () => {
expect(() => divide(10, 0)).toThrow("Division by zero");
});
});
});
```
### Pattern 2: Testing Classes
```typescript
// services/user.service.ts
export class UserService {
private users: Map<string, User> = new Map();
create(user: User): User {
if (this.users.has(user.id)) {
throw new Error("User already exists");
}
this.users.set(user.id, user);
return user;
}
findById(id: string): User | undefined {
return this.users.get(id);
}
update(id: string, updates: Partial<User>): User {
const user = this.users.get(id);
if (!user) {
throw new Error("User not found");
}
const updated = { ...user, ...updates };
this.users.set(id, updated);
return updated;
}
delete(id: string): boolean {
return this.users.delete(id);
}
}
// services/user.service.test.ts
import { describe, it, expect, beforeEach } from "vitest";
import { UserService } from "./user.service";
describe("UserService", () => {
let service: UserService;
beforeEach(() => {
service = new UserService();
});
describe("create", () => {
it("should create a new user", () => {
const user = { id: "1", name: "John", email: "john@example.com" };
const created = service.create(user);
expect(created).toEqual(user);
expect(service.findById("1")).toEqual(user);
});
it("should throw error if user already exists", () => {
const user = { id: "1", name: "John", email: "john@example.com" };
service.create(user);
expect(() => service.create(user)).toThrow("User already exists");
});
});
describe("update", () => {
it("should update existing user", () => {
const user = { id: "1", name: "John", email: "john@example.com" };
service.create(user);
const updated = service.update("1", { name: "Jane" });
expect(updated.name).toBe("Jane");
expect(updated.email).toBe("john@example.com");
});
it("should throw error if user not found", () => {
expect(() => service.update("999", { name: "Jane" })).toThrow(
"User not found",
);
});
});
});
```
### Pattern 3: Testing Async Functions
```typescript
// services/api.service.ts
export class ApiService {
async fetchUser(id: string): Promise<User> {
const response = await fetch(`https://api.example.com/users/${id}`);
if (!response.ok) {
throw new Error("User not found");
}
return response.json();
}
async createUser(user: CreateUserDTO): Promise<User> {
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(user),
});
return response.json();
}
}
// services/api.service.test.ts
import { describe, it, expect, vi, beforeEach } from "vitest";
import { ApiService } from "./api.service";
// Mock fetch globally
global.fetch = vi.fn();
describe("ApiService", () => {
let service: ApiService;
beforeEach(() => {
service = new ApiService();
vi.clearAllMocks();
});
describe("fetchUser", () => {
it("should fetch user successfully", async () => {
const mockUser = { id: "1", name: "John", email: "john@example.com" };
(fetch as any).mockResolvedValueOnce({
ok: true,
json: async () => mockUser,
});
const user = await service.fetchUser("1");
expect(user).toEqual(mockUser);
expect(fetch).toHaveBeenCalledWith("https://api.example.com/users/1");
});
it("should throw error if user not found", async () => {
(fetch as any).mockResolvedValueOnce({
ok: false,
});
await expect(service.fetchUser("999")).rejects.toThrow("User not found");
});
});
describe("createUser", () => {
it("should create user successfully", async () => {
const newUser = { name: "John", email: "john@example.com" };
const createdUser = { id: "1", ...newUser };
(fetch as any).mockResolvedValueOnce({
ok: true,
json: async () => createdUser,
});
const user = await service.createUser(newUser);
expect(user).toEqual(createdUser);
expect(fetch).toHaveBeenCalledWith(
"https://api.example.com/users",
expect.objectContaining({
method: "POST",
body: JSON.stringify(newUser),
}),
);
});
});
});
```
## Mocking Patterns
### Pattern 1: Mocking Modules
```typescript
// services/email.service.ts
import nodemailer from "nodemailer";
export class EmailService {
private transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
async sendEmail(to: string, subject: string, html: string) {
await this.transporter.sendMail({
from: process.env.EMAIL_FROM,
to,
subject,
html,
});
}
}
// services/email.service.test.ts
import { describe, it, expect, vi, beforeEach } from "vitest";
import { EmailService } from "./email.service";
vi.mock("nodemailer", () => ({
default: {
createTransport: vi.fn(() => ({
sendMail: vi.fn().mockResolvedValue({ messageId: "123" }),
})),
},
}));
describe("EmailService", () => {
let service: EmailService;
beforeEach(() => {
service = new EmailService();
});
it("should send email successfully", async () => {
await service.sendEmail(
"test@example.com",
"Test Subject",
"<p>Test Body</p>",
);
expect(service["transporter"].sendMail).toHaveBeenCalledWith(
expect.objectContaining({
to: "test@example.com",
subject: "Test Subject",
}),
);
});
});
```
### Pattern 2: Dependency Injection for Testing
```typescript
// services/user.service.ts
export interface IUserRepository {
findById(id: string): Promise<User | null>;
create(user: User): Promise<User>;
}
export class UserService {
constructor(private userRepository: IUserRepository) {}
async getUser(id: string): Promise<User> {
const user = await this.userRepository.findById(id);
if (!user) {
throw new Error("User not found");
}
return user;
}
async createUser(userData: CreateUserDTO): Promise<User> {
// Business logic here
const user = { id: generateId(), ...userData };
return this.userRepository.create(user);
}
}
// services/user.service.test.ts
import { describe, it, expect, vi, beforeEach } from "vitest";
import { UserService, IUserRepository } from "./user.service";
describe("UserService", () => {
let service: UserService;
let mockRepository: IUserRepository;
beforeEach(() => {
mockRepository = {
findById: vi.fn(),
create: vi.fn(),
};
service = new UserService(mockRepository);
});
describe("getUser", () => {
it("should return user if found", async () => {
const mockUser = { id: "1", name: "John", email: "john@example.com" };
vi.mocked(mockRepository.findById).mockResolvedValue(mockUser);
const user = await service.getUser("1");
expect(user).toEqual(mockUser);
expect(mockRepository.findById).toHaveBeenCalledWith("1");
});
it("should throw error if user not found", async () => {
vi.mocked(mockRepository.findById).mockResolvedValue(null);
await expect(service.getUser("999")).rejects.toThrow("User not found");
});
});
describe("createUser", () => {
it("should create user successfully", async () => {
const userData = { name: "John", email: "john@example.com" };
const createdUser = { id: "1", ...userData };
vi.mocked(mockRepository.create).mockResolvedValue(createdUser);
const user = await service.createUser(userData);
expect(user).toEqual(createdUser);
expect(mockRepository.create).toHaveBeenCalled();
});
});
});
```
### Pattern 3: Spying on Functions
```typescript
// utils/logger.ts
export const logger = {
info: (message: string) => console.log(`INFO: ${message}`),
error: (message: string) => console.error(`ERROR: ${message}`),
};
// services/order.service.ts
import { logger } from "../utils/logger";
export class OrderService {
async processOrder(orderId: string): Promise<void> {
logger.info(`Processing order ${orderId}`);
// Process order logic
logger.info(`Order ${orderId} processed successfully`);
}
}
// services/order.service.test.ts
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { OrderService } from "./order.service";
import { logger } from "../utils/logger";
describe("OrderService", () => {
let service: OrderService;
let loggerSpy: any;
beforeEach(() => {
service = new OrderService();
loggerSpy = vi.spyOn(logger, "info");
});
afterEach(() => {
loggerSpy.mockRestore();
});
it("should log order processing", async () => {
await service.processOrder("123");
expect(loggerSpy).toHaveBeenCalledWith("Processing order 123");
expect(loggerSpy).toHaveBeenCalledWith("Order 123 processed successfully");
expect(loggerSpy).toHaveBeenCalledTimes(2);
});
});
```
## Integration Testing
- ### Pattern 1: API Integration Tests
-
- ```typescript
- // tests/integration/user.api.test.ts
- import request from "supertest";
- import { app } from "../../src/app";
- import { pool } from "../../src/config/database";
-
- describe("User API Integration Tests", () => {
- beforeAll(async () => {
- // Setup test database
- await pool.query("CREATE TABLE IF NOT EXISTS users (...)");
- });
-
- afterAll(async () => {
- // Cleanup
- await pool.query("DROP TABLE IF EXISTS users");
- await pool.end();
- });
-
- beforeEach(async () => {
- // Clear data before each test
- await pool.query("TRUNCATE TABLE users CASCADE");
- });
-
- describe("POST /api/users", () => {
- it("should create a new user", async () => {
- const userData = {
- name: "John Doe",
- email: "john@example.com",
- password: "password123",
- };
-
- const response = await request(app)
- .post("/api/users")
- .send(userData)
- .expect(201);
-
- expect(response.body).toMatchObject({
- name: userData.name,
- email: userData.email,
- });
- expect(response.body).toHaveProperty("id");
- expect(response.body).not.toHaveProperty("password");
- });
-
- it("should return 400 if email is invalid", async () => {
- const userData = {
- name: "John Doe",
- email: "invalid-email",
- password: "password123",
- };
-
- const response = await request(app)
- .post("/api/users")
- .send(userData)
- .expect(400);
-
- expect(response.body).toHaveProperty("error");
- });
-
- it("should return 409 if email already exists", async () => {
- const userData = {
- name: "John Doe",
- email: "john@example.com",
- password: "password123",
- };
-
- await request(app).post("/api/users").send(userData);
-
- const response = await request(app)
- .post("/api/users")
- .send(userData)
- .expect(409);
-
- expect(response.body.error).toContain("already exists");
- });
- });
-
- describe("GET /api/users/:id", () => {
- it("should get user by id", async () => {
- const createResponse = await request(app).post("/api/users").send({
- name: "John Doe",
- email: "john@example.com",
- password: "password123",
- });
-
- const userId = createResponse.body.id;
-
- const response = await request(app)
- .get(`/api/users/${userId}`)
- .expect(200);
-
- expect(response.body).toMatchObject({
- id: userId,
- name: "John Doe",
- email: "john@example.com",
- });
- });
-
- it("should return 404 if user not found", async () => {
- await request(app).get("/api/users/999").expect(404);
- });
- });
-
- describe("Authentication", () => {
- it("should require authentication for protected routes", async () => {
- await request(app).get("/api/users/me").expect(401);
- });
-
- it("should allow access with valid token", async () => {
- // Create user and login
- await request(app).post("/api/users").send({
- name: "John Doe",
- email: "john@example.com",
- password: "password123",
- });
-
- const loginResponse = await request(app).post("/api/auth/login").send({
- email: "john@example.com",
- password: "password123",
- });
-
- const token = loginResponse.body.token;
-
- const response = await request(app)
- .get("/api/users/me")
- .set("Authorization", `Bearer ${token}`)
- .expect(200);
-
- expect(response.body.email).toBe("john@example.com");
- });
- });
- });
- ```
-
- ### Pattern 2: Database Integration Tests
-
- ```typescript
- // tests/integration/user.repository.test.ts
- import { describe, it, expect, beforeAll, afterAll, beforeEach } from "vitest";
- import { Pool } from "pg";
- import { UserRepository } from "../../src/repositories/user.repository";
-
- describe("UserRepository Integration Tests", () => {
- let pool: Pool;
- let repository: UserRepository;
-
- beforeAll(async () => {
- pool = new Pool({
- host: "localhost",
- port: 5432,
- database: "test_db",
- user: "test_user",
- password: "test_password",
- });
-
- repository = new UserRepository(pool);
-
- // Create tables
- await pool.query(`
- CREATE TABLE IF NOT EXISTS users (
- id SERIAL PRIMARY KEY,
- name VARCHAR(255) NOT NULL,
- email VARCHAR(255) UNIQUE NOT NULL,
- password VARCHAR(255) NOT NULL,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
- )
- `);
- });
-
- afterAll(async () => {
- await pool.query("DROP TABLE IF EXISTS users");
- await pool.end();
- });
-
- beforeEach(async () => {
- await pool.query("TRUNCATE TABLE users CASCADE");
- });
-
- it("should create a user", async () => {
- const user = await repository.create({
- name: "John Doe",
- email: "john@example.com",
- password: "hashed_password",
- });
-
- expect(user).toHaveProperty("id");
- expect(user.name).toBe("John Doe");
- expect(user.email).toBe("john@example.com");
- });
-
- it("should find user by email", async () => {
- await repository.create({
- name: "John Doe",
- email: "john@example.com",
- password: "hashed_password",
- });
-
- const user = await repository.findByEmail("john@example.com");
-
- expect(user).toBeTruthy();
- expect(user?.name).toBe("John Doe");
- });
+ Integration tests verify real database operations and HTTP endpoints using `supertest` and a test database instance. Always truncate tables in `beforeEach` and tear down in `afterAll`.
- it("should return null if user not found", async () => {
- const user = await repository.findByEmail("nonexistent@example.com");
- expect(user).toBeNull();
- });
- });
- ```
+ For full API integration test examples (supertest + PostgreSQL) and database repository integration tests, see [references/advanced-testing-patterns.md](references/advanced-testing-patterns.md).
## Frontend Testing with Testing Library
- ### Pattern 1: React Component Testing
-
- ```typescript
- // components/UserForm.tsx
- import { useState } from 'react';
-
- interface Props {
- onSubmit: (user: { name: string; email: string }) => void;
- }
-
- export function UserForm({ onSubmit }: Props) {
- const [name, setName] = useState('');
- const [email, setEmail] = useState('');
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- onSubmit({ name, email });
- };
-
- return (
- <form onSubmit={handleSubmit}>
- <input
- type="text"
- placeholder="Name"
- value={name}
- onChange={(e) => setName(e.target.value)}
- data-testid="name-input"
- />
- <input
- type="email"
- placeholder="Email"
- value={email}
- onChange={(e) => setEmail(e.target.value)}
- data-testid="email-input"
- />
- <button type="submit">Submit</button>
- </form>
- );
- }
-
- // components/UserForm.test.tsx
- import { render, screen, fireEvent } from '@testing-library/react';
- import { describe, it, expect, vi } from 'vitest';
- import { UserForm } from './UserForm';
-
- describe('UserForm', () => {
- it('should render form inputs', () => {
- render(<UserForm onSubmit={vi.fn()} />);
-
- expect(screen.getByPlaceholderText('Name')).toBeInTheDocument();
- expect(screen.getByPlaceholderText('Email')).toBeInTheDocument();
- expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument();
- });
-
- it('should update input values', () => {
- render(<UserForm onSubmit={vi.fn()} />);
-
- const nameInput = screen.getByTestId('name-input') as HTMLInputElement;
- const emailInput = screen.getByTestId('email-input') as HTMLInputElement;
-
- fireEvent.change(nameInput, { target: { value: 'John Doe' } });
- fireEvent.change(emailInput, { target: { value: 'john@example.com' } });
-
- expect(nameInput.value).toBe('John Doe');
- expect(emailInput.value).toBe('john@example.com');
- });
-
- it('should call onSubmit with form data', () => {
- const onSubmit = vi.fn();
- render(<UserForm onSubmit={onSubmit} />);
-
- fireEvent.change(screen.getByTestId('name-input'), {
- target: { value: 'John Doe' },
- });
- fireEvent.change(screen.getByTestId('email-input'), {
- target: { value: 'john@example.com' },
- });
- fireEvent.click(screen.getByRole('button', { name: 'Submit' }));
-
- expect(onSubmit).toHaveBeenCalledWith({
- name: 'John Doe',
- email: 'john@example.com',
- });
- });
- });
- ```
-
- ### Pattern 2: Testing Hooks
-
- ```typescript
- // hooks/useCounter.ts
- import { useState, useCallback } from "react";
-
- export function useCounter(initialValue = 0) {
- const [count, setCount] = useState(initialValue);
-
- const increment = useCallback(() => setCount((c) => c + 1), []);
- const decrement = useCallback(() => setCount((c) => c - 1), []);
- const reset = useCallback(() => setCount(initialValue), [initialValue]);
-
- return { count, increment, decrement, reset };
- }
-
- // hooks/useCounter.test.ts
- import { renderHook, act } from "@testing-library/react";
- import { describe, it, expect } from "vitest";
- import { useCounter } from "./useCounter";
-
- describe("useCounter", () => {
- it("should initialize with default value", () => {
- const { result } = renderHook(() => useCounter());
- expect(result.current.count).toBe(0);
- });
-
- it("should initialize with custom value", () => {
- const { result } = renderHook(() => useCounter(10));
- expect(result.current.count).toBe(10);
- });
-
- it("should increment count", () => {
- const { result } = renderHook(() => useCounter());
-
- act(() => {
- result.current.increment();
- });
-
- expect(result.current.count).toBe(1);
- });
-
- it("should decrement count", () => {
- const { result } = renderHook(() => useCounter(5));
-
- act(() => {
- result.current.decrement();
- });
-
- expect(result.current.count).toBe(4);
- });
-
- it("should reset to initial value", () => {
- const { result } = renderHook(() => useCounter(10));
-
- act(() => {
- result.current.increment();
- result.current.increment();
- });
-
- expect(result.current.count).toBe(12);
-
- act(() => {
- result.current.reset();
- });
+ Test React components by rendering them and querying by role, placeholder, or test ID. Test hooks with `renderHook` + `act`. Prefer semantic queries (`getByRole`, `getByPlaceholderText`) over `data-testid`.
- expect(result.current.count).toBe(10);
- });
- });
- ```
+ For complete React component test examples (UserForm, hooks with `renderHook`/`act`), see [references/advanced-testing-patterns.md](references/advanced-testing-patterns.md).
## Test Fixtures and Factories
+ Use `@faker-js/faker` to generate realistic test data factories. Factories accept optional `overrides` so tests can set only the fields they care about:
+
```typescript
// tests/fixtures/user.fixture.ts
import { faker } from "@faker-js/faker";
export function createUserFixture(overrides?: Partial<User>): User {
return {
id: faker.string.uuid(),
name: faker.person.fullName(),
email: faker.internet.email(),
createdAt: faker.date.past(),
...overrides,
};
}
-
- export function createUsersFixture(count: number): User[] {
- return Array.from({ length: count }, () => createUserFixture());
- }
-
- // Usage in tests
- import {
- createUserFixture,
- createUsersFixture,
- } from "../fixtures/user.fixture";
-
- describe("UserService", () => {
- it("should process user", () => {
- const user = createUserFixture({ name: "John Doe" });
- // Use user in test
- });
-
- it("should handle multiple users", () => {
- const users = createUsersFixture(10);
- // Use users in test
- });
- });
```
- ## Snapshot Testing
-
- ```typescript
- // components/UserCard.test.tsx
- import { render } from '@testing-library/react';
- import { describe, it, expect } from 'vitest';
- import { UserCard } from './UserCard';
-
- describe('UserCard', () => {
- it('should match snapshot', () => {
- const user = {
- id: '1',
- name: 'John Doe',
- email: 'john@example.com',
- avatar: 'https://example.com/avatar.jpg',
- };
-
- const { container } = render(<UserCard user={user} />);
-
- expect(container.firstChild).toMatchSnapshot();
- });
-
- it('should match snapshot with loading state', () => {
- const { container } = render(<UserCard loading />);
- expect(container.firstChild).toMatchSnapshot();
- });
- });
- ```
-
- ## Coverage Reports
-
- ```typescript
- // package.json
- {
- "scripts": {
- "test": "vitest",
- "test:coverage": "vitest --coverage",
- "test:ui": "vitest --ui"
- }
- }
- ```
+ For snapshot testing, coverage configuration, test organization patterns, promise testing, and timer mocking, see [references/advanced-testing-patterns.md](references/advanced-testing-patterns.md).
## Best Practices
1. **Follow AAA Pattern**: Arrange, Act, Assert
2. **One assertion per test**: Or logically related assertions
3. **Descriptive test names**: Should describe what is being tested
4. **Use beforeEach/afterEach**: For setup and teardown
5. **Mock external dependencies**: Keep tests isolated
6. **Test edge cases**: Not just happy paths
7. **Avoid implementation details**: Test behavior, not implementation
8. **Use test factories**: For consistent test data
9. **Keep tests fast**: Mock slow operations
10. **Write tests first (TDD)**: When possible
11. **Maintain test coverage**: Aim for 80%+ coverage
12. **Use TypeScript**: For type-safe tests
13. **Test error handling**: Not just success cases
14. **Use data-testid sparingly**: Prefer semantic queries
15. **Clean up after tests**: Prevent test pollution
-
- ## Common Patterns
-
- ### Test Organization
-
- ```typescript
- describe("UserService", () => {
- describe("createUser", () => {
- it("should create user successfully", () => {});
- it("should throw error if email exists", () => {});
- it("should hash password", () => {});
- });
-
- describe("updateUser", () => {
- it("should update user", () => {});
- it("should throw error if not found", () => {});
- });
- });
- ```
-
- ### Testing Promises
-
- ```typescript
- // Using async/await
- it("should fetch user", async () => {
- const user = await service.fetchUser("1");
- expect(user).toBeDefined();
- });
-
- // Testing rejections
- it("should throw error", async () => {
- await expect(service.fetchUser("invalid")).rejects.toThrow("Not found");
- });
- ```
-
- ### Testing Timers
-
- ```typescript
- import { vi } from "vitest";
-
- it("should call function after delay", () => {
- vi.useFakeTimers();
-
- const callback = vi.fn();
- setTimeout(callback, 1000);
-
- expect(callback).not.toHaveBeenCalled();
-
- vi.advanceTimersByTime(1000);
-
- expect(callback).toHaveBeenCalled();
-
- vi.useRealTimers();
- });
- ```