Nest.js 統合テスト（integration）テストコードを作成する

`Nest.js`では、公式に用意されたテストユーティリティ `(@nestjs/testing) `と`Supertest`を組み合わせることで、統合テスト（`integration test`）を簡単に作成できます。以下は基本的な手順とサンプルコードです。

# テスト用モジュールの作成

- `TestingModule`の作成:
テスト用に対象のモジュール（例えば`AppModule`）をインポートし、`Test.createTestingModule()`を使ってテスト用モジュールを作成します。

- Nest アプリケーションインスタンスの生成:
コンパイル後、`moduleFixture.createNestApplication()`で`Nest`アプリケーションのインスタンスを作成し、`app.init()`を呼び出して初期化します。

# エンドポイントのテスト

## `Supertest`を利用したエンドポイントのテスト

- `HTTP`リクエストの送信:
`app.getHttpServer()`を用いて、`Supertest`で`HTTP`リクエストをシミュレートし、実際のエンドポイントの動作を確認します。

- レスポンスの検証:
ステータスコードやレスポンスボディを`.expect()`メソッドで検証します。

- サンプルコード

以下は、`GET/`エンドポイントに対する統合テストのサンプルコードです。

```
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('AppController (Integration Test)', () => {
  let app: INestApplication;

  // テスト実行前にアプリケーションをセットアップ
  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  // エンドポイントのテスト例
  it('/ (GET)', () => {
    return request(app.getHttpServer())
      .get('/')
      .expect(200)
      .expect('Hello World!');
  });

  // テスト終了後にアプリケーションをクローズ
  afterAll(async () => {
    await app.close();
  });
});
```

## ポイント解説

- `TestingModule`の活用:
`Test.createTestingModule`を使うことで、実際のアプリケーションの`DI`コンテナやミドルウェア、ガードなどを含む状態でテスト環境を再現できます。

- `Supertest`の利用:
`Supertest`によって、`HTTP`リクエストを模擬し、`API`のエンドツーエンド動作を検証可能です。これは、統合テストとして非常に有用です。

- 後処理:
テスト終了後は `app.close()`を呼び出してリソースを解放し、次のテストへの影響を防ぎます。

- 拡張例:
データベースとの連携がある場合は、テスト用の`DB`環境を用意したり、モックを利用するなどしてテストを実行します。

# `e2e`テストコードを作成

- エンドツーエンド（e2e）テスト:
アプリケーション全体（ルーティング、ミドルウェア、ガード、コントローラー、サービスなど）が実際に動作しているかを検証します。

- 利用するライブラリ:
`@nestjs/testing`でアプリケーションのテスト用インスタンスを作成し、`HTTP`リクエストのシミュレーションには`Supertest`を使用します。

## `e2e`テストのセットアップ

通常、`e2e`テストはプロジェクト内の`e2e`フォルダに配置され、専用の`Jest`設定（例: `jest-e2e.json`）で実行されます。例えば、`package.json`のスクリプトに以下のように記述します。

```
{
  "scripts": {
    "test:e2e": "jest --config ./test/jest-e2e.json"
  }
}
```

jest-e2e.json のサンプル設定は以下の通りです。

```
{
  "moduleFileExtensions": ["js", "json", "ts"],
  "rootDir": "../",
  "testRegex": ".e2e-spec.ts$",
  "transform": {
    "^.+\\.(t|j)s$": "ts-jest"
  },
  "coverageDirectory": "./coverage",
  "testEnvironment": "node"
}
```

## サンプルコード

以下は、`GET/`エンドポイントに対する`e2e`テストのサンプルコードです。アプリケーション全体をテスト用モジュールとしてセットアップし、`Supertest`を使って`HTTP`リクエストをシミュレートしています。

```
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('App E2E Test', () => {
  let app: INestApplication;

  // テスト実行前にアプリケーションを初期化
  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  // GET / エンドポイントのテスト
  it('GET / should return "Hello World!"', () => {
    return request(app.getHttpServer())
      .get('/')
      .expect(200)
      .expect('Hello World!');
  });

  // テスト終了後にアプリケーションをクローズ
  afterAll(async () => {
    await app.close();
  });
});
```

このようにして`Supertest`と`e2e`テストを実装することで、実際の`HTTP`リクエストに基づいたエンドポイントの動作検証が可能となり、アプリケーション全体の品質向上に寄与します。

今日は以上です。

ありがとうございました。
よろしくお願いいたします。