@neoma/managed-database
Version:
A managed database fixture for Jest testing (unit, integration etc)
263 lines (189 loc) • 7.25 kB
Markdown
# @neoma/managed-database
A managed database fixture for NestJS integration testing with TypeORM. Automatically handles in-memory SQLite database lifecycle for Jest tests.
[](https://www.npmjs.com/package/@neoma/managed-database)
[](https://opensource.org/licenses/MIT)
## Motivation
Integration testing NestJS applications with TypeORM requires setting up and tearing down database connections for each test. This package eliminates the boilerplate by:
- Automatically managing a single DataSource instance across tests
- Using in-memory SQLite for fast, isolated test execution
- Handling initialization and cleanup via Jest lifecycle hooks
- Auto-discovering entities from your project structure
## Problem / Solution
### Without @neoma/managed-database
```typescript
import { DataSource } from "typeorm"
import { User } from "./user.entity"
import { Post } from "./post.entity"
describe("UserRepository", () => {
let dataSource: DataSource
beforeEach(async () => {
// Manually create and initialize datasource for each test
dataSource = new DataSource({
type: "sqlite",
database: ":memory:",
entities: [User, Post], // Must manually list all entities
synchronize: true,
})
await dataSource.initialize()
})
afterEach(async () => {
// Manually destroy datasource after each test
await dataSource.destroy()
})
it("should create a user", async () => {
const repo = dataSource.getRepository(User)
// Test code...
})
})
```
### With @neoma/managed-database
```typescript
import { managedDatasourceInstance } from "@neoma/managed-database"
import { User } from "./user.entity"
describe("UserRepository", () => {
it("should create a user", async () => {
// Get the managed instance - lifecycle handled automatically!
const dataSource = managedDatasourceInstance()
const repo = dataSource.getRepository(User)
const user = await repo.save({ username: "alice" })
expect(user.id).toBeDefined()
})
it("gets a fresh database for each test", async () => {
// Each test starts with a clean database
const dataSource = managedDatasourceInstance()
const repo = dataSource.getRepository(User)
const users = await repo.find()
expect(users).toHaveLength(0) // Always starts empty
})
})
```
## Installation
### 1. Install the package
```bash
npm install --save-dev @neoma/managed-database
```
### 2. Install peer dependencies
```bash
npm install --save-dev sqlite3 typeorm @nestjs/typeorm
npm install @nestjs/common @nestjs/core
```
### 3. Import in your test files
```typescript
import { managedDatasourceInstance } from "@neoma/managed-database"
```
That's it! The package automatically registers Jest hooks when imported, so your database lifecycle is managed automatically.
## Basic Usage
### Repository Testing
```typescript
import { managedDatasourceInstance } from "@neoma/managed-database"
import { User } from "./user.entity"
describe("User Repository", () => {
it("should create and retrieve users", async () => {
const dataSource = managedDatasourceInstance()
const userRepo = dataSource.getRepository(User)
const user = await userRepo.save({
username: "alice",
})
const found = await userRepo.findOne({ where: { id: user.id } })
expect(found.username).toBe("alice")
})
})
```
### Testing Services
```typescript
import { managedDatasourceInstance } from "@neoma/managed-database"
import { UserService } from "./user.service"
import { User } from "./user.entity"
describe("UserService", () => {
let service: UserService
beforeEach(() => {
const dataSource = managedDatasourceInstance()
service = new UserService(dataSource.getRepository(User))
})
it("should create users", async () => {
const user = await service.create({ username: "alice" })
expect(user.id).toBeDefined()
})
})
```
## API Reference
### `managedDatasourceInstance(): DataSource`
Returns the managed DataSource instance for the current test.
**Returns:** `DataSource` - A TypeORM DataSource instance configured with in-memory SQLite
**Lifecycle:**
- Automatically initialized before each test via `beforeEach` hook
- Automatically destroyed after each test via `afterEach` hook
- Returns the same instance within a single test
- Returns a fresh instance for each new test
**Example:**
```typescript
const dataSource = managedDatasourceInstance()
const repo = dataSource.getRepository(User)
```
**Note:** The lifecycle hooks are registered automatically when you import anything from this package, so you don't need to call any setup functions.
### `datasource(): Promise<DataSource>`
Low-level function to create a new DataSource instance. Used internally by `managedDatasourceInstance()`.
**Returns:** `Promise<DataSource>` - A new, initialized DataSource instance
**Configuration:**
- Type: SQLite
- Database: In-memory (`:memory:`)
- Entities: Auto-discovered from `src/**/*.entity.ts`
- Synchronize: Enabled (auto-creates schema)
## Configuration
### Entity Discovery
The package automatically discovers entities from:
```
src/**/*.entity.ts
```
This pattern will find all entity files in your `src` directory and subdirectories. Ensure your entities:
1. Use the `.entity.ts` naming convention
2. Are located under the `src/` directory
3. Export classes decorated with `@Entity()`
**Example Structure:**
```
src/
├── user.entity.ts
├── post.entity.ts
└── modules/
└── auth/
└── session.entity.ts
```
All three entities (User, Post, Session) will be automatically discovered.
### Database Configuration
The package uses the following TypeORM configuration:
```typescript
{
type: "sqlite",
database: ":memory:",
entities: ["src/**/*.entity.ts"],
synchronize: true,
}
```
**Key Features:**
- **SQLite In-Memory:** Each test gets a completely isolated database
- **Auto-Sync:** Schema is automatically created from your entities
- **Fast:** In-memory databases are extremely fast for testing
- **Clean State:** Every test starts with a fresh, empty database
## Why SQLite In-Memory?
Testing with SQLite in-memory provides several advantages:
1. **Speed:** Orders of magnitude faster than traditional databases
2. **Isolation:** Each test gets a completely independent database
3. **Portability:** No external database setup required
4. **CI/CD Friendly:** Works out of the box in any environment
5. **Consistency:** Deterministic test behavior
## Requirements
- Node.js >= 22.0.0
- Jest testing framework
- TypeScript >= 5.0
- NestJS >= 11.0
- TypeORM >= 0.3
## Links
- [npm package](https://www.npmjs.com/package/@neoma/managed-database)
- [GitHub repository](https://github.com/shipdventures/neoma-managed-database)
- [Report Issues](https://github.com/shipdventures/neoma-managed-database/issues)
## Related Packages
- [@neoma/managed-app](https://www.npmjs.com/package/@neoma/managed-app) - Managed NestJS app instances for E2E testing
## License
MIT
---
Built with love by [Shipdventures](https://github.com/shipdventures) for the NestJS community.