メインコンテンツまでスキップ

Integration Testing

For integration tests that require external services like Redis or PostgreSQL, use Testcontainers. Zelt provides pre-configured container configs that integrate with the lifecycle system.

Installation

pnpm add -D @zeltjs/testing testcontainers

Redis Integration Testing

import { describe, it, expect } from 'vitest';
import { createTestTarget } from '@zeltjs/testing/vitest';
import { RedisTestContainerConfig } from '@zeltjs/testing/redis';
import { CacheService } from './cache.service';

describe('CacheService', () => {
it('should cache values in Redis', async () => {
const { target } = await createTestTarget(CacheService, {
configs: [RedisTestContainerConfig],
});

await target.set('key', 'value');
const result = await target.get('key');

expect(result).toBe('value');
});
});

RedisTestContainerConfig automatically:

  • Starts a Redis container before tests
  • Provides connection URL to services depending on RedisConfig
  • Stops and cleans up the container after tests

Custom Container Config

Create your own container config by implementing the Lifecycle interface:

import { Config, inject, LifecycleManager, type Lifecycle } from '@zeltjs/core';
import { GenericContainer, type StartedTestContainer } from 'testcontainers';

@Config
export class PostgresTestContainerConfig implements Lifecycle {
private container: StartedTestContainer | undefined;
private connectionUrl = '';

constructor(lifecycle = inject(LifecycleManager)) {
lifecycle.register(this);
}

async startup(): Promise<void> {
this.container = await new GenericContainer('postgres:16-alpine')
.withEnvironment({
POSTGRES_USER: 'test',
POSTGRES_PASSWORD: 'test',
POSTGRES_DB: 'testdb',
})
.withExposedPorts(5432)
.start();

const host = this.container.getHost();
const port = this.container.getMappedPort(5432);
this.connectionUrl = `postgres://test:test@${host}:${port}/testdb`;
}

async shutdown(): Promise<void> {
await this.container?.stop();
}

get url(): string {
return this.connectionUrl;
}
}

Sociable Unit Tests

Integration tests with Testcontainers are ideal for "Sociable Unit Tests" — testing units that collaborate with real dependencies rather than mocks:

import { describe, it, expect } from 'vitest';
import { createTestTarget } from '@zeltjs/testing/vitest';
import { RedisTestContainerConfig } from '@zeltjs/testing/redis';
import { SessionService } from './session.service';
import { UserService } from './user.service';

describe('SessionService with real Redis', () => {
it('should persist session across service calls', async () => {
const { target, get } = await createTestTarget(SessionService, {
configs: [RedisTestContainerConfig],
});

const userService = get(UserService);
const session = await target.create({ userId: '123' });

const user = await userService.fromSession(session.id);
expect(user.id).toBe('123');
});
});