针对实际数据库测试 NestJS 服务

Tom*_*ala 8 javascript node.js typescript typeorm nestjs

我希望能够针对实际数据库测试我的 Nest 服务。我知道大多数单元测试应该使用模拟对象,但有时也可以对数据库本身进行测试。

我已经搜索了 SO 和 Nest 的 GH 问题,并且开始达到所有答案的传递闭包。:-)

我正在尝试从https://github.com/nestjs/nest/issues/363#issuecomment-360105413工作。以下是我的单元测试,它使用自定义提供程序将存储库传递给我的服务类。

describe("DepartmentService", () => {
  const token = getRepositoryToken(Department);
  let service: DepartmentService;
  let repo: Repository<Department>;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        DepartmentService,
        {
          provide: token,
          useClass: Repository
        }
      ]
    }).compile();

    service = module.get<DepartmentService>(DepartmentService);
    repo = module.get(token);
  });
Run Code Online (Sandbox Code Playgroud)

一切都正确编译,TypeScript 似乎很高兴。但是,当我尝试执行createsave我的 Repository实例上执行时,底层Repository似乎未定义。这是堆栈回溯:

TypeError: Cannot read property 'create' of undefined

  at Repository.Object.<anonymous>.Repository.create (repository/Repository.ts:99:29)
  at DepartmentService.<anonymous> (relational/department/department.service.ts:46:53)
  at relational/department/department.service.ts:19:71
  at Object.<anonymous>.__awaiter (relational/department/department.service.ts:15:12)
  at DepartmentService.addDepartment (relational/department/department.service.ts:56:16)
  at Object.<anonymous> (relational/department/test/department.service.spec.ts:46:35)
  at relational/department/test/department.service.spec.ts:7:71
Run Code Online (Sandbox Code Playgroud)

看起来EntityManagerTypeORMRepository类的实例没有被初始化;这是undefined这个回溯抱怨的参考。

我如何获得RepositoryEntityManager正确初始化?

谢谢,汤姆。

ken*_*ley 11

@nestjs/testing为了简单起见,我宁愿不使用。

首先,创建一个可重用的助手:

/* src/utils/testing-helpers/createMemDB.js */
import { createConnection, EntitySchema } from 'typeorm'
type Entity = Function | string | EntitySchema<any>

export async function createMemDB(entities: Entity[]) {
  return createConnection({
    // name, // let TypeORM manage the connections
    type: 'sqlite',
    database: ':memory:',
    entities,
    dropSchema: true,
    synchronize: true,
    logging: false
  })
}
Run Code Online (Sandbox Code Playgroud)

然后,写测试:

/* src/user/user.service.spec.ts */
import { Connection, Repository } from 'typeorm'
import { createMemDB } from '../utils/testing-helpers/createMemDB'
import UserService from './user.service'
import User from './user.entity'

describe('User Service', () => {
  let db: Connection
  let userService: UserService
  let userRepository: Repository<User>

  beforeAll(async () => {
    db = await createMemDB([User])
    userRepository = await db.getRepository(User)
    userService = new UserService(userRepository) // <--- manually inject
  })
  afterAll(() => db.close())

  it('should create a new user', async () => {
    const username = 'HelloWorld'
    const password = 'password'

    const newUser = await userService.createUser({ username, password })
    expect(newUser.id).toBeDefined()

    const newUserInDB = await userRepository.findOne(newUser.id)
    expect(newUserInDB.username).toBe(username)
  })
})
Run Code Online (Sandbox Code Playgroud)

参考https://github.com/typeorm/typeorm/issues/1267#issuecomment-483775861


Kim*_*ern 10

要正确初始化 typeorm,您应该能够TypeOrmModule在测试中导入:

Test.createTestingModule({
  imports: [
   TypeOrmModule.forRoot({
        type: 'mysql',
        // ...
   }),
   TypeOrmModule.forFeature([Department])
  ]
Run Code Online (Sandbox Code Playgroud)

  • 这是朝着正确方向迈出的一大步!这似乎会导致每次代码运行时都会创建一个新的数据库连接。那是对的吗?如果是这样,关闭连接以运行`module.close()`的正确方法是什么?您是否建议在“beforeAll”或“beforeEach”中设置模块? (2认同)

Tom*_*ala 5

这是采用 Kim Kern 建议的测试的更新。

describe("DepartmentService", () => {
  let service: DepartmentService;
  let repo: Repository<Department>;
  let module: TestingModule;

  beforeAll(async () => {
    module = await Test.createTestingModule({
      imports: [
        TypeOrmModule.forRoot(),
        TypeOrmModule.forFeature([Department])
      ],
      providers: [DepartmentService]
    }).compile();

    service = module.get<DepartmentService>(DepartmentService);
    repo = module.get<Repository<Department>>(getRepositoryToken(Department));
  });

  afterAll(async () => {
    module.close();
  });

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

  // ...
}
Run Code Online (Sandbox Code Playgroud)