Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
Mocks are fake functions that replace real implementations for testing. Spies track how functions are called without changing their behavior. Both help test code in isolation.
Replace real function with fake implementation
Control return values and behavior
Watch function calls without changing it
Track calls, arguments, and return values
Test one unit without dependencies (APIs, databases, files)
Fast tests - no network calls or slow operations
Simulate errors, edge cases, and specific scenarios
// Using Jest
import { jest } from '@jest/globals';
// Create a mock function
const mockFn = jest.fn();
// Call it
mockFn('hello');
mockFn('world', 42);
// Assert it was called
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(2);
// Assert specific arguments
expect(mockFn).toHaveBeenCalledWith('hello');
expect(mockFn).toHaveBeenLastCalledWith('world', 42);
// Check all calls
console.log(mockFn.mock.calls);
// [['hello'], ['world', 42]]
// Using Vitest
import { vi } from 'vitest';
const mockFn2 = vi.fn();
// Same API as Jest!// Single return value
const mockFn = jest.fn().mockReturnValue(42);
console.log(mockFn()); // 42
console.log(mockFn()); // 42 (always returns 42)
// Different values for each call
const mockFn2 = jest.fn()
.mockReturnValueOnce('first')
.mockReturnValueOnce('second')
.mockReturnValue('default');
console.log(mockFn2()); // 'first'
console.log(mockFn2()); // 'second'
console.log(mockFn2()); // 'default'
console.log(mockFn2()); // 'default'
// Mock implementation
const mockAdd = jest.fn((a, b) => a + b);
console.log(mockAdd(2, 3)); // 5
expect(mockAdd).toHaveBeenCalledWith(2, 3);
// Mock async function
const mockAsync = jest.fn()
.mockResolvedValue({ data: 'success' });
const result = await mockAsync();
console.log(result); // { data: 'success' }
// Mock rejected promise
const mockError = jest.fn()
.mockRejectedValue(new Error('Failed'));
try {
await mockError();
} catch (e) {
console.log(e.message); // 'Failed'
}Watches existing function
Replaces entire function
// Spy on object method
const calculator = {
add: (a, b) => a + b,
multiply: (a, b) => a * b
};
// Create spy (Jest)
const addSpy = jest.spyOn(calculator, 'add');
// Original function still works
const result = calculator.add(2, 3);
console.log(result); // 5
// But we can track calls
expect(addSpy).toHaveBeenCalled();
expect(addSpy).toHaveBeenCalledWith(2, 3);
// Can also override return value
addSpy.mockReturnValue(100);
console.log(calculator.add(2, 3)); // 100 (mocked)
// Restore original
addSpy.mockRestore();
console.log(calculator.add(2, 3)); // 5 (original)
// Vitest spy
import { vi } from 'vitest';
const multiplySpy = vi.spyOn(calculator, 'multiply');
calculator.multiply(4, 5);
expect(multiplySpy).toHaveBeenCalledWith(4, 5);// api.js
export async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// user-service.js
import { fetchUser } from './api';
export async function getUserName(id) {
const user = await fetchUser(id);
return user.name;
}
// user-service.test.js
import { fetchUser } from './api';
import { getUserName } from './user-service';
// Mock the entire module
jest.mock('./api');
test('gets user name', async () => {
// Mock the fetchUser function
fetchUser.mockResolvedValue({
id: 1,
name: 'Alice',
email: 'alice@example.com'
});
const name = await getUserName(1);
expect(name).toBe('Alice');
expect(fetchUser).toHaveBeenCalledWith(1);
});
// Vitest module mocking
import { vi } from 'vitest';
vi.mock('./api', () => ({
fetchUser: vi.fn()
}));// Using Jest
global.fetch = jest.fn();
test('fetches user data', async () => {
// Mock successful response
fetch.mockResolvedValueOnce({
ok: true,
json: async () => ({ name: 'Alice', age: 25 })
});
const response = await fetch('/api/user');
const data = await response.json();
expect(data.name).toBe('Alice');
expect(fetch).toHaveBeenCalledWith('/api/user');
});
test('handles fetch error', async () => {
// Mock failed response
fetch.mockResolvedValueOnce({
ok: false,
status: 404,
json: async () => ({ error: 'Not found' })
});
const response = await fetch('/api/user');
expect(response.ok).toBe(false);
expect(response.status).toBe(404);
});
test('handles network error', async () => {
// Mock network failure
fetch.mockRejectedValueOnce(new Error('Network error'));
await expect(fetch('/api/user')).rejects.toThrow('Network error');
});
// Clean up after tests
afterEach(() => {
fetch.mockClear();
});jest.useFakeTimers() / vi.useFakeTimers()jest.setSystemTime(new Date('2024-01-01'))jest.mock('./module', () => ({ ...actualModule, foo: mockFoo }))jest.mock('./MyClass')// Function that uses setTimeout
function delayedGreeting(name, callback) {
setTimeout(() => {
callback(`Hello, ${name}!`);
}, 1000);
}
test('calls callback after delay', () => {
// Enable fake timers
jest.useFakeTimers();
const callback = jest.fn();
delayedGreeting('Alice', callback);
// Callback not called yet
expect(callback).not.toHaveBeenCalled();
// Fast-forward time by 1 second
jest.advanceTimersByTime(1000);
// Now callback should be called
expect(callback).toHaveBeenCalledWith('Hello, Alice!');
// Restore real timers
jest.useRealTimers();
});
// Test setInterval
test('polls every second', () => {
jest.useFakeTimers();
const callback = jest.fn();
const intervalId = setInterval(callback, 1000);
// No calls yet
expect(callback).toHaveBeenCalledTimes(0);
// Advance 1 second
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(1);
// Advance 2 more seconds
jest.advanceTimersByTime(2000);
expect(callback).toHaveBeenCalledTimes(3);
clearInterval(intervalId);
jest.useRealTimers();
});// database.js
export class Database {
connect() {
// Real database connection
}
async query(sql) {
// Real database query
}
}
// user-repository.js
import { Database } from './database';
export class UserRepository {
constructor() {
this.db = new Database();
}
async findUser(id) {
await this.db.connect();
return this.db.query(`SELECT * FROM users WHERE id = ${id}`);
}
}
// user-repository.test.js
import { Database } from './database';
import { UserRepository } from './user-repository';
// Mock the entire Database class
jest.mock('./database');
test('finds user by id', async () => {
// Mock database methods
const mockConnect = jest.fn();
const mockQuery = jest.fn().mockResolvedValue({
id: 1,
name: 'Alice'
});
// Make Database constructor return mock instance
Database.mockImplementation(() => ({
connect: mockConnect,
query: mockQuery
}));
const repo = new UserRepository();
const user = await repo.findUser(1);
expect(mockConnect).toHaveBeenCalled();
expect(mockQuery).toHaveBeenCalledWith('SELECT * FROM users WHERE id = 1');
expect(user.name).toBe('Alice');
});Create fake implementations
Control return values
Track calls and arguments
Original function still runs
APIs, databases, external services
Fast, isolated tests
Clear between tests
Restore originals