NestJSのUnitTestでsqlite3を使う方法

UnitTestの時にDBにつなぐのではなくMockデーターベース(sqlite)を起動させMockデーターベースに接続する方法
ソースコードの大半はNestJSのDatabaseの項目を参考にしています。

https://docs.nestjs.com/techniques/database

1 対象のアプリを実装していきます

  • アプリを作成
    nest new app

  • 依存ライブラリのインストール
    npm install --save @nestjs/typeorm typeorm mysql2

  • app.moduleの更新

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: [],
      synchronize: true,
    }),
  ],
})
export class AppModule {}
  • User moduleを作成
nest g  mo user
nest g s user
  • User entityを作成
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column({ default: true })
  isActive: boolean;
}
  • User moduleを更新
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entitiy';
import { UserService } from './user.service';

@Module({
  providers: [UserService],
  imports: [TypeOrmModule.forFeature([User])],
})
export class UserModule {}
  • UserServiceにロジックを追加
export class UserService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }

  findOne(id: string): Promise<User> {
    return this.usersRepository.findOne(id);
  }

  async remove(id: string): Promise<void> {
    await this.usersRepository.delete(id);
  }
}

2 UnitTest 今回Mockのデータベースにsqliteを利用します
npm install --save slqite3

ちなみに今のUserServiceのテストはこのようになっています。 この状況でnpm run testを行ってもテストは通りません。

describe('UserService', () => {
  let service: UserService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [UserService],
    }).compile();

    service = module.get<UserService>(UserService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });
});

次のように変更するとテストが通るようになります。

      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          useClass: Repository,
        },
      ],

次に接続先のDBをmysqlからsqliteに変更します。

describe('UserService', () => {
  let service: UserService;
  let repository: Repository<User>;
  const testConnectionName = 'testConnection';

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          useClass: Repository,
        },
      ],
    }).compile();
    // mock用DBの作成
    const connection = await createConnection({
      type: 'sqlite',
      database: ':memory:', // inmemoryで動かす
      dropSchema: true,
      entities: [User],
      synchronize: true,
      logging: false,
      name: testConnectionName,
    });
    repository = getRepository(User, testConnectionName);
    // 
    service = new UserService(repository);
  });

  // テストが終わる度にMockDBをクリーンにする
  afterEach(async () => {
    await getConnection(testConnectionName).close();
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });
});

あとはこんな感じでテストを書くだけ

  describe('find', () => {
    it('find', async () => {
      const user: User = {
        id: 1,
        firstName: 'test',
        lastName: 'user',
        isActive: true,
      };
      await repository.save(user);
      const inUsers = await service.findAll();
      expect(inUsers.length).toBe(1);
      expect(inUsers[0].firstName).toBe(user.firstName);
    });
  });

今回のコード

github.com