Skip to content

Testing

The API uses Vitest for testing with a focus on unit and integration tests.

Running Tests

bash
pnpm test           # Run all tests
pnpm test:watch     # Watch mode
pnpm test:run       # Run once (CI)

Single File

bash
pnpm test src/services/__tests__/BookService.test.ts

Pattern Matching

bash
pnpm test --grep "BookService"

Test Structure

Tests live alongside their source files in __tests__ directories:

src/
├── services/
│   ├── __tests__/
│   │   └── BookService.test.ts
│   └── BookService.ts

Writing Tests

Basic Test

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

describe("BookService", () => {
	let service: BookService
	let mockRepository: BookRepository

	beforeEach(() => {
		mockRepository = {
			findById: vi.fn(),
			create: vi.fn(),
		} as unknown as BookRepository

		service = new BookService(mockRepository)
	})

	it("should find a book by id", async () => {
		const book = { id: "1", title: "Test Book" }
		vi.mocked(mockRepository.findById).mockResolvedValue(book)

		const result = await service.findById("1")

		expect(result).toEqual(book)
		expect(mockRepository.findById).toHaveBeenCalledWith("1")
	})
})

Testing Controllers

typescript
import { createRequest, createResponse } from "node-mocks-http"

describe("BookController", () => {
	it("should return 200 for GET /books", async () => {
		const req = createRequest({ method: "GET", url: "/books" })
		const res = createResponse()

		await controller.getBooks(req, res)

		expect(res.statusCode).toBe(200)
	})
})

Testing with Database

For integration tests, use a test database:

typescript
import { DataSource } from "typeorm"

describe("BookRepository Integration", () => {
	let dataSource: DataSource

	beforeAll(async () => {
		dataSource = await createTestDataSource()
	})

	afterAll(async () => {
		await dataSource.destroy()
	})

	beforeEach(async () => {
		await dataSource.synchronize(true) // Reset database
	})
})

Mocking

Vitest Mocks

typescript
import { vi } from "vitest"

const mockService = {
	findAll: vi.fn().mockResolvedValue([]),
	create: vi.fn().mockImplementation((data) => ({ id: "1", ...data })),
}

Spying

typescript
const spy = vi.spyOn(service, "create")
await controller.createBook(data)
expect(spy).toHaveBeenCalledWith(data)

Test Utilities

Factory Functions

Create test data with factories:

typescript
// test/factories/book.ts
export const createBook = (overrides = {}): Book => ({
	id: "test-id",
	title: "Test Book",
	createdAt: new Date(),
	...overrides,
})

Assertions

typescript
expect(result).toBeDefined()
expect(result).toHaveLength(3)
expect(result).toContainEqual(expected)
expect(fn).toHaveBeenCalledTimes(1)
expect(fn).toHaveBeenCalledWith(arg1, arg2)

Code Coverage

bash
pnpm test --coverage

Coverage reports are generated in coverage/.

Built with VitePress