LoginSignup
6
3

More than 3 years have passed since last update.

nestjs、typeORMでカスタムリポジトリのテストを書く

Last updated at Posted at 2020-04-26

やりたいこと

nestjs、typeORMの環境でtypeORMのカスタムリポジトリのテストを書きたい。
テストは以下のような感じで実行したい。

  • テストデータを用意し、実際に処理を動かしてテストしたい(カスタムリポジトリの関数をmockするテストはしたくない。)
  • テストの度にテストデータはリセットされるようにしたい(各テストごとにテストデータを用意する。テストデータの共有はしたくない)

これの参考になるようなコードがあまりネットに見つからなかったので残しておく。

コード

テスト対象のentity、repository

このentityのテストを書いていく。

  • 外部キーが貼られている
  • ユニークキーが貼られている
import { Entity, Column, PrimaryGeneratedColumn, JoinColumn, ManyToOne, Unique } from 'typeorm';
import { ItemSku } from './itemSku';

@Entity('user_deliverd_sku_relations')
@Unique(['email', 'item_sku_id'])
export class UserDeliverdSkuRelation {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({
    nullable: false,
  })
  email: string;

  @Column({
    nullable: false,
  })
  item_sku_id: number;

  @ManyToOne((type) => ItemSku)
  @JoinColumn({ name: 'item_sku_id' })
  item_sku: ItemSku;

  @Column({
    nullable: false,
    default: () => 'CURRENT_TIMESTAMP',
  })
  created_at: Date;

  @Column({
    nullable: false,
    default: () => 'CURRENT_TIMESTAMP',
  })
  updated_at: Date;
}

import { Repository, EntityRepository } from 'typeorm';
import { UserDeliverdSkuRelation } from '../../entity/userDeliverdSkuRelation';

@EntityRepository(UserDeliverdSkuRelation)
export class DeliveredItemSkuRepository extends Repository<UserDeliverdSkuRelation> {
  findByEmail(email: string) {
    return this.find({
      where: {
        email,
      },
    });
  }
}

リポジトリのテストコード

できたテストコードがこれ


import { createConnection, getConnection } from 'typeorm';
import { UserDeliverdSkuRelation } from '../../entity/userDeliverdSkuRelation';
import { ItemSku } from '../../entity/itemSku';
import { DeliveredItemSkuService } from '../../service/deliveredItemSku/service';
import { DeliveredItemSkuRepository } from '../../repository/deliveredItemSku';
import { ItemSkuRepository } from '../../repository/itemSku';
import dbConfig from '../../../src/config/database';

describe('DeliveredItemSkuRepository', () => {
  let deliveredItemSkuService: DeliveredItemSkuService;
  let deliveredItemSkuRepository: DeliveredItemSkuRepository;
  let itemSkuRepository: ItemSkuRepository;

  beforeEach(async () => {
    const connection = await createConnection({
      type: dbConfig.type,
      host: dbConfig.host,
      port: parseInt(dbConfig.port, 10),
      username: dbConfig.username,
      password: dbConfig.password,
      database: dbConfig.database,
      entities: [UserDeliverdSkuRelation, ItemSku],
      synchronize: true,
      dropSchema: true,
      logging: false,
      name: dbConfig.connectionName,
    });

    deliveredItemSkuRepository = connection.getCustomRepository(DeliveredItemSkuRepository);
    deliveredItemSkuService = new DeliveredItemSkuService(deliveredItemSkuRepository, connection);

    itemSkuRepository = connection.getCustomRepository(ItemSkuRepository);
  });

  afterEach(async () => {
    await deliveredItemSkuRepository.delete({});
    await itemSkuRepository.delete({});

    await getConnection(dbConfig.connectionName).close();
  });

  describe('定義されている', () => {
    it('リポジトリが定義されている', () => {
      expect(deliveredItemSkuRepository).toBeInstanceOf(DeliveredItemSkuRepository);
    });
  });

  describe('findByEmail', () => {
    it('指定したemailのUserDeliverdSkuRelationエンティティの配列が返ってくる', async () => {
      const testData = [
        {
          id: 1,
          email: 'hoge@example.com',
          item_sku_id: 1,
        },
        {
          id: 1,
          email: 'bar@example.com',
          item_sku_id: 1,
        },
      ] as UserDeliverdSkuRelation[];

      await itemSkuRepository.insert({
        id: 1,
      });

      await deliveredItemSkuRepository.insert(testData);

      const res = await deliveredItemSkuRepository.findByEmail('hoge@example.com');

      expect(res.length).toEqual(1);
      expect(res[0]).toMatchObject({
        id: 1,
        email: 'hoge@example.com',
        item_sku_id: 1,
      });
    });
  });

  describe('insert', () => {
    describe('できるパターン', () => {
      it('insertしたレコードが確認できる', async () => {
        const testData = {
          id: 1,
          email: 'hoge@example.com',
          item_sku_id: 1,
        } as UserDeliverdSkuRelation;

        await itemSkuRepository.insert({
          id: 1,
        });

        await deliveredItemSkuRepository.insert(testData);

        expect(await deliveredItemSkuService.findAll()).toEqual([testData]);
      });
    });

    describe('できないパターン', () => {
      it('外部キー制約でinsertに失敗する。(ひもづくitem_skusがないため)', async () => {
        const testData = {
          id: 1,
          email: 'hoge@example.com',
          item_sku_id: 1,
        } as UserDeliverdSkuRelation;

        await expect(deliveredItemSkuRepository.insert(testData)).rejects.toThrow(
          /insert or update on table "user_deliverd_sku_relations" violates foreign key*/,
        );
      });

      it('ユニークキー制約でinsertに失敗する。(emailとitem_sku_idでユニーク)', async () => {
        await itemSkuRepository.insert({
          id: 1,
        });

        const testData1 = {
          id: 1,
          email: 'hoge@example.com',
          item_sku_id: 1,
        } as UserDeliverdSkuRelation;

        await deliveredItemSkuRepository.insert(testData1);

        const testData2 = {
          id: 2,
          email: 'hoge@example.com',
          item_sku_id: 1,
        } as UserDeliverdSkuRelation;

        await expect(deliveredItemSkuRepository.insert(testData2)).rejects.toThrow(
          /duplicate key value violates unique constraint*/,
        );
      });
    });
  });
});

ポイント

各テスト実行前

beforeEachで以下を行なっている。

  beforeEach(async () => {
    const connection = await createConnection({
      type: dbConfig.type,
      host: dbConfig.host,
      port: parseInt(dbConfig.port, 10),
      username: dbConfig.username,
      password: dbConfig.password,
      database: dbConfig.database,
      entities: [UserDeliverdSkuRelation, ItemSku],
      synchronize: true,
      dropSchema: true,
      logging: false,
      name: dbConfig.connectionName,
    });

これによって、以下がテストの度に実行されるようになる。

  • DBとの接続
    createConnectionで接続し直す

  • テーブルのdrop
    dropSchema: true によって接続が確立される度にテーブルをdropする。対象はentitiesに記載されたentity。

  • マイグレーション
    synchronize: true によってマイグレーションを流し直す。

これでテストのたびにまっさらなテーブル(UserDeliverdSkuRelation, ItemSku)が用意されるようになる。

各テスト実行後

afterEachでテストに関わるentityのデータを削除をする。
beforeEachの処理によって、テスト実行前のテストデータ削除はできているが、テスト実行後にもテストデータを削除しておくことで他のテストへ影響を与えないようにする。

  afterEach(async () => {
    await deliveredItemSkuRepository.delete({});
    await itemSkuRepository.delete({});

    await getConnection(dbConfig.connectionName).close();
  });

これで当初やりたかった テストの度にテストデータはリセットされる ができてると思う。

参考

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