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

テスト

Zeltはアプリケーションをテストするためのユーティリティを含む@zeltjs/testingパッケージを提供しています。依存性注入のサポートとTestcontainers統合が含まれています。

インストール

pnpm add -D @zeltjs/testing vitest

createTestTarget

createTestTargetは、依存性注入を使用してサービスをインスタンス化するための主要なテストユーティリティです。ライフサイクル管理とクリーンアップを自動的に処理します。

import { describe, it, expect } from 'vitest';
import { createTestTarget } from '@zeltjs/testing';
import { UserService } from './user.service';
import { ProcessEnvConfig } from '@zeltjs/core';

describe('UserService', () => {
it('should create user', async () => {
const { target, shutdown } = await createTestTarget(UserService, {
configs: [ProcessEnvConfig],
});

const user = await target.create({ name: 'Alice' });
expect(user.name).toBe('Alice');
});
});

オプション

オプション説明
configsClass[]登録する設定クラス
overridesOverride[]依存関係のモック実装

戻り値

プロパティ説明
targetTインスタンス化されたサービス
get(cls) => Tコンテナから追加の依存関係を解決
shutdown() => Promise<void>クリーンアップ関数(vitestのafterAllで自動呼び出し)

依存関係のモック

overridesを使用して実際の実装をモックに置き換えます:

import { createTestTarget } from '@zeltjs/testing';
import { UserService } from './user.service';
import { EmailService } from './email.service';

describe('UserService', () => {
it('should send welcome email', async () => {
const mockEmailService = {
send: vi.fn().mockResolvedValue(undefined),
};

const { target } = await createTestTarget(UserService, {
overrides: [
{ provide: EmailService, useValue: mockEmailService },
],
});

await target.register({ email: 'alice@example.com' });
expect(mockEmailService.send).toHaveBeenCalledWith(
'alice@example.com',
expect.stringContaining('Welcome')
);
});
});

HTTPテスト

Honoの組み込みリクエストヘルパーを使用してアプリケーションのHTTPエンドポイントをテストします:

import { describe, it, expect } from 'vitest';
import { app } from './app';

describe('Hello API', () => {
it('should return greeting', async () => {
const res = await app.request('/hello/world');

expect(res.status).toBe(200);
const body = await res.json();
expect(body).toEqual({ message: 'Hello, world!' });
});
});

型安全なクライアントでのテスト

生成されたAppTypeとHonoのクライアントを使用して、完全に型付けされたテストを行います。AppTypeの生成方法はOpenAPIと型生成を参照してください。

import { hc } from 'hono/client';
import { describe, it, expect } from 'vitest';
import { app } from './app';
import type { AppType } from './generated/app.gen';

describe('Hello API', () => {
const client = hc<AppType>('http://localhost', {
fetch: (input, init) => app.fetch(new Request(input, init)),
});

it('should return greeting with type safety', async () => {
const res = await client.hello[':name'].$get({
param: { name: 'world' }
});

expect(res.status).toBe(200);
const body = await res.json();
expect(body.message).toBe('Hello, world!');
});
});

Testcontainers統合

Redisなどの外部サービスを必要とする統合テストでは、Testcontainersを使用します。Zeltはライフサイクルシステムと統合された事前設定済みのコンテナ設定を提供しています。

Redis統合テスト

pnpm add -D @zeltjs/testing testcontainers
import { describe, it, expect } from 'vitest';
import { createTestTarget } from '@zeltjs/testing';
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は自動的に以下を行います:

  • テスト前にRedisコンテナを起動
  • RedisConfigに依存するサービスに接続URLを提供
  • テスト後にコンテナを停止してクリーンアップ

カスタムコンテナ設定

Lifecycleインターフェースを実装して独自のコンテナ設定を作成:

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;
}
}

ライフサイクル管理

createTestTargetは依存関係のライフサイクルを自動的に管理します:

  1. 起動: すべての登録されたLifecycle実装がテスト前に起動
  2. シャットダウン: vitestのafterAllフックでクリーンアップが自動呼び出し

これにより、テストが失敗してもTestcontainersやその他のリソースが適切にクリーンアップされます。