LoginSignup
11
3

More than 3 years have passed since last update.

ts-jest + TypeORM + TypeORMSeedingでts-nodeテスト環境を構築する方法

Posted at

はじめに

最近バックエンドにts-node+TypeORMを用いて開発することが多いのですが、ts-node+TypeORM使用時のテスト環境構築の情報がなかなかないと感じたので、今回共有しようと思います。

※前提条件

今回はテスト環境の構築のみを記述するので、ts-nodeでのRESTAPIサーバーorGraphQLサーバーの構築方法等は省略します。
またすでにtestという名前のデータベースを作成済みで、TypeORMにて以下のようにデータベースのテーブルを構築済みとします。

Entities/User.ts
@Entity('users')
export class User extends BaseEntity {

    @Column()
    username: string;

    @Column()
    email: string;

    @Column()
    password: string;

}

環境構築

1・ライブラリのインストール

Typescript

yarn add (or npm i) -D Typescript @types/node ts-node

TypeORM

yarn add (or npm i) typeorm typeorm-seeding

Jest

yarn add (or npm i) -D jest ts-jest @types/jest

その他

yarn add (or npm i) faker

yarn add (or npm i) -D @types/faker

yarn add (or npm i) dotenv

2・TypeORM、データベースの設定

まず、テスト時に予め作成しておいたテスト用のDB(今回の場合testと名付けたDB)に接続するようにTypeORMの設定を変更する必要があります。
公式ではcreateConnectionsConnectionManagerで複数の接続を管理する方法が紹介されていますが、試してみた所上手く動作しなかったので、今回はormconfig.jsと三項演算子でテスト時にテスト用のDBに接続する方法を紹介します。
また、今回はdotenvで環境変数を読み込むのと三項演算子を使用するためにjson形式ではなくjs形式でファイルを設定します。

まずはormconfig.jsに以下のように設定を記述します。ormconfig.jsonがすでに存在している場合はormconfig.jsに名称を変えて下さい。

ormconfig.js
module.exports = {
    type: process.env.DATABASE_TYPE_DEVELOPMENT,
    host: process.env.DATABASE_HOST_DEVELOPMENT,
    port: process.env.DATABASE_PORT_DEVELOPMENT,
    username: process.env.DATABASE_USERNAME_DEVELOPMENT,
    password: process.env.DATABASE_PASSWORD_DEVELOPMENT,
    database:
        process.env.NODE_ENV === 'test'
            ? process.env.DATABASE_NAME_TEST
            : process.env.DATABASE_NAME_DEVELOPMENT,
    logging: false,
    synchronize: process.env.NODE_ENV === 'test' ? true : false,
    dropSchema: process.env.NODE_ENV === 'test' ? true : false,
    entities:
        process.env.NODE_ENV === 'test'
            ? ['src/entities/**/*.ts']
            : ['dist/entities/**/*.js'],
    migrations: ['dist/migration/**/*.js'],
    subscribers: ['dist/subscriber/**/*.js'],
    cli: {
        entitiesDir: 'src/entities',
        migrationsDir: 'src/migration',
        subscribersDir: 'src/subscriber',
    },
};

上記の設定ではNODE_ENV === 'test'時にdatabaseはテスト用のDBに接続し、synchronizedropSchematrueに(syncronizeをtrueにするとmigration runコマンドを入力しなくても自動的にスキーマの変更がDBに反映され、dropSchemaをtrueにすると接続時にDBテーブル内のデータを全て消去してくれます)、entitiesはテスト時は.tsファイルを参照するようにしています。
各人環境の違いがあると思うので、それぞれお好みで設定を変えて下さい。

3・TypeORMSeedingの設定

次にTypeormSeedingの設定を行います。
TypeORMSeedingはTypeORM用の疑似データ(シードデータ)を作成するライブラリで、コードベースで簡単にシードデータを作成、管理することが可能になります。
TypeORMSeeding公式Githubページ
今回の例ではテスト用にUserエンティティの疑似データを10件作成します。

まず初めに、TypeORMSeedingでは疑似データを作成するためにTypeORMのエンティティを基にした疑似データの構造体であるfactoryを定義する必要があります。
データはfakerを使用してランダムなデータを生成しています。

user.factory.ts
import { define } from 'typeorm-seeding';
import * as Faker from 'faker/locale/ja';

import { User } from './entities/User';

define(User, (faker: typeof Faker): User => {
    const user = new User();
     //↓ライブラリ「faker」でランダムなデータを作成する
        user.username = faker.internet.username();
        user.email = faker.internet.email();
        user.password = faker.internet.password();

    return user;
});

export default User;

次にこのUser factoryを基に擬似データを作成するシーダーを作成します。

user.seed.ts
import { Factory, Seeder } from 'typeorm-seeding';

import User from '../factories/user.factory';

export class CreateUsersSeed implements Seeder {
    public async run(factory: Factory) {
        await factory(User)().createMany(20);
    }
}

これで擬似データを作成する準備が整いました。
作成はyarn seed:runのように起動スクリプトを記述してcliコマンドで作成する方法もあるのですが今回作成する疑似データはテスト環境用のため、cliコマンドを入力して疑似データを最初に一度作るだけではあるテスト結果が他のテスト結果にも影響を及ぼす可能性があるので、今回は
(テスト毎に)
DB接続&DB内のデータをクリア

疑似データ作成
(全てのテスト終了後)
DB内のデータをクリア&DB接続終了
というプロセスをテスト毎に行い、それぞれのテスト結果が他のテストに影響を及ぼさないように設定したいと思います。TypeORMSeedingにはテスト用に使用できるuseSeeding()useRefreshDatabase()などの便利なメソッドが用意されており、これらを使用することでcliコマンドを入力せずとも局所的に擬似データを挿入することが可能になります。
具体的な方法は後述します。

4・ts-jestの設定

Typescriptで開発をしている場合にjestでテストを記述する際はBabelts-jestを使用してテストコードをトランスパイルする必要が出てきます。
今回はts-jestを使用して環境構築をしていきます。
必要なライブラリをインストールした後に

yarn ts-jest config:init

このコマンドを入力すればプロジェクト内にjest.config.jsが作成されます。
次にpackage.jsonにテスト起動のnpmスクリプトを記述します。

"scripts": {
    "test": "NODE_ENV=test jest --runInBand",
}

ポイントはjestに--runInBandオプションを渡している点です。
jestはデフォルトでは複数のテストファイルを並行実行する仕様になっているため、もしプロジェクトに複数のテストファイルを用意していてかつその全てのファイルが同じDBに接続するような状況ではエラーを起こしてしまいます。
今回の例ではテストファイルを一つしか用意しておりませんが、今後ファイルが増えて複数のテストファイルがテスト用のDBに接続するような状況になった時には--runInBandオプションをわたして逐次実行(ファイルを一つずつ順に実行していく処理)に切り替えたほうが良いです。
↓参考資料
Jestテストの並行実行と逐次実行をちゃんと理解する

5・テストコードの記述

最後にテストコードを記述します。

user.test.ts

import {
    runSeeder,
    tearDownDatabase,
    useRefreshDatabase,
    useSeeding,
} from 'typeorm-seeding';


import { CreateUsersSeed } from './seeds/user.seed';


beforeEach(async () => {
     //DBに接続&内部のデータをクリア
    await useRefreshDatabase();
        //プロジェクト内のfactoryをロードする
    await useSeeding();
        //シーダーを実行する
    await runSeeder(CreateUsersSeed);
});

afterAll(async () => {
       //DBとの接続を終了する
    await tearDownDatabase();
});


describe('test', () => {
   //テストコードを記述
})

このようになります。
jestを実行する際にNODE_ENV=testを指定しているため、useRefreshDatabaseでDBに接続する時にはormconfig.jsの設定どおりテスト用のDBに接続されるようになっています。
あとはテストコードを記述するだけです!

まとめ

テスト環境の構築は機能をモックすることや疑似データを用意することがなかなか困難ですが、Jest+TypeORM+TypeORMSeedingを使用すれば多少は楽にテスト環境が構築できるのではないかと思います。
今後もTypeORMやJestに関する情報を共有していきたいと思います。

11
3
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
11
3