LoginSignup
5
2

More than 1 year has passed since last update.

【NestJS】結合テストを実装する(ConfigModule, cross-env)

Last updated at Posted at 2021-10-15

はじめに

単体テストに引き続き、結合テストを実装しました。

実装したコードを確認したい方は以下よりご確認ください。

実装

ユーザの動作の流れをテストするために、各モジュールを組み合わせた結合テストを作成します。

これまでCookieSessionやValidationPipeの設定はmain.ts内で行っていましたが、テストを実行するときにmain.tsは実行されないので、これらの設定をAppModule側に移してあげる必要があります。

まずValidationPipeについては、@Module()providersAppServiceの下に記述を加えます。

app.module.ts
  providers: [
    AppService,
    {
      provide: APP_PIPE,
      useValue: new ValidationPipe({
        whitelist: true,
      }),
    },
  ],

cookieSessionについては、AppModuleクラス内に以下の記述を加えます。

app.module.ts
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(
      cookieSession({
        keys: ['key1'],
      }),
    );
  }
}

以下のテストケースで新規登録に関する結合テストを作成すると以下のようになります。

  • /auth/signupにPOSTリクエストを投げて(新規登録を行って)、登録したユーザのidとemailがレスポンスとして返ってくるかどうか
  • 新規登録後に/auth/whoamiにGETリクエストを投げて、ログインユーザのemailがレスポンスとして返ってくるかどうか
auth.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('Authentication System', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

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

  it('handles a signup request', () => {
    const email = 'asdlkjqdaa@akl.com';

    return request(app.getHttpServer())
      .post('/auth/signup')
      .send({ email, password: 'alsdk' })
      .expect(201)
      .then((res) => {
        const { id, email } = res.body;
        expect(id).toBeDefined();
        expect(email).toEqual(email);
      });
  });

  it('signup as a new user then get the currency logged in user', async () => {
    const email = 'asdf@asdf.com';

    const res = await request(app.getHttpServer())
      .post('/auth/signup')
      .send({ email, password: 'asdf' })
      .expect(201);

    const cookie = res.get('Set-Cookie');

    const { body } = await request(app.getHttpServer())
      .get('/auth/whoami')
      .set('Cookie', cookie)
      .expect(200);

    expect(body.email).toEqual(email);
  });
});

無事にテストが完成したようにみえますが、改善すべき点がいくつか存在します。

テストデータ用のDBを用意する

現在の設定だと、テスト環境でもdb.sqliteを使用しているため、テストデータと本番データが混ざってしまいます。
そのため、テスト時には使用するDBを切り替えるような設定が必要となります。

まずは.env.development.env.testファイルを作成し、DB_NAMEをそれぞれ設定します。

.env.development
DB_NAME=db.sqlite
.env.test
DB_NAME=test.sqlite

これらの環境変数を取得する際にConfigModuleおよびConfigServiceを使用するため、@nest/configをインストールします。

npm install @nestjs/config

ConfigModule.forRoot()でenvのファイル名を指定します。
環境によって参照するファイルを切り替えるために、process.env.NODE_ENVを使用しています。

app.module.ts
@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.env.${process.env.NODE_ENV}`,
    }),
    TypeOrmModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (config: ConfigService) => {
        return {
          type: 'sqlite',
          database: config.get<string>('DB_NAME'),
          entities: [User, Report],
          synchronize: true,
        };
      },
    }),

npmスクリプトを実行する際に任意の環境変数を設定するパッケージcross-envをインストールします。

npm install cross-env

cross-env NODE_ENV=testなどをスクリプトに加えてあげることで環境変数が設定されます。

package.json
    "start": "cross-env NODE_ENV=development nest start",
    "start:dev": "cross-env NODE_ENV=development nest start --watch",
    "start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "cross-env NODE_ENV=test jest --watch --maxWorkers=1",
    "test:cov": "cross-env NODE_ENV=test jest --coverage",
    "test:debug": "cross-env NODE_ENV=test node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "cross-env NODE_ENV=test jest --config ./test/jest-e2e.json"

試しにnpm run test:e2eでテストを実行してみると、以下のエラーがでます。
スクリーンショット 2021-10-13 22.14.15.png
このエラーは記述した2つのテストで各々がtest.sqliteに接続しようとしているために起こります。
test:e2eのスクリプトに--maxWorkers=1を加えることで、接続がバッティングしないようになります。

"test:e2e": "cross-env NODE_ENV=test jest --config ./test/jest-e2e.json --maxWorkers=1"

テストごとにDBにデータがたまらないようにする

新規登録のテストを複数回行ったとき、1度目にユーザデータが作成されるため、2度目以降は失敗します。
テストの回数によって結果が変わってしまうのはよくないので、テストが終わったらDBを元の状態に戻すようにします。

setup.tsを作成し、テスト前global.beforeEach()でDBを削除する処理を記述します。
(node v14.14.0以降であればawait rm(join(__dirname, '..', 'test.sqlite'));)

そして、テスト後global.afterEach()にはDBへの接続を切断する処理を記述します。

setup.ts
import { join } from 'path';
import { promises } from 'fs';
import { getConnection } from 'typeorm';

global.beforeEach(async () => {
  try {
    await promises.unlink(join(__dirname, '..', 'test.sqlite'));
  } catch (err) {}
});

global.afterEach(async () => {
  const conn = getConnection();
  await conn.close();
});

jest-e2e.json"setupFilesAfterEnv": ["<rootDir>/setup.ts"]を追記すれば設定完了です。

参考資料

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2