3

给定一个通过 @InjectQueue 装饰器使用队列的 Injectable:

@Injectable()
export class EnqueuerService {
  constructor (
    @InjectQueue(QUEUE_NAME) private readonly queue: Queue
  ) {
  }

  async foo () {
    return this.queue.add('job')
  }
}

如何测试此服务是否正确调用队列?我可以做基本的脚手架:

describe('EnqueuerService', () => {
  let module: TestingModule
  let enqueuerService: EnqueuerService

  beforeAll(async () => {
    module = await Test.createTestingModule({
      imports: [EnqueuerModule]
    }).compile()
    enqueuerService = module.get(EnqueuerService)

    // Here I'd usually pull in the dependency to test against:
    // queue = module.get(QUEUE_NAME)
    //
    // (but this doesn't work because queue is using the @InjectQueue decorator)
  })

  afterAll(async () => await module.close())

  describe('#foo', () => {
    it('adds a job', async () => {
      await enqueuerService.foo()

      // Something like this would be nice: 
      // expect(queue.add).toBeCalledTimes(1)
      //
      // (but maybe there are alternative ways that are easier?)
    })
  })
})

我在 Nest DI 容器设置中迷失了方向,但我怀疑有一些聪明的方法可以做到这一点。但是,尽管尝试了数小时,但我无法取得进展,并且文档对我没有帮助。任何人都可以提供解决方案吗?它不必嘲笑,如果更容易创建一个真正的队列来测试它也很好,我只想按预期验证我的服务排队!任何帮助表示赞赏。

4

1 回答 1

5

通过查看库,我发现了一个帮助函数,它根据其名称创建队列注入令牌:getQueueToken(name?: string).

该函数是从库中导出的,因此您可以使用它来为您提供自己的队列实现以进行测试。

import { getQueueToken } from '@nestjs/bull';

describe('EnqueuerService', () => {
  let module: TestingModule
  let enqueuerService: EnqueuerService

  beforeAll(async () => {
    module = await Test.createTestingModule({
      imports: [EnqueuerModule]
    })
      .overrideProvider(getQueueToken(QUEUE_NAME))
      .useValue({ /* mocked queue */ })
      .compile()

    enqueuerService = module.get(EnqueuerService)
    queue = module.get(QUEUE_NAME)
  })

  afterAll(async () => await module.close())

  describe('#foo', () => {
    it('adds a job', async () => {
      await enqueuerService.foo()

      expect(queue.add).toBeCalledTimes(1)
    })
  })
})

编辑

要检查队列服务调用的方法,您可以在测试文件的根目录中创建一个模拟。我重新组织了测试文件,以便在测试设置和测试本身之间有一个更清晰的分隔:

let module: TestingModule
let enqueuerService: EnqueuerService
let mockQueue;

// Create a helper function to create the app.
const createApp = async () => {
  module = await Test.createTestingModule({
    imports: [EnqueuerModule]
  })
    .overrideProvider(getQueueToken(QUEUE_NAME))
    .useValue(mockQueue)
    .compile()

  enqueuerService = module.get(EnqueuerService)
}

describe('EnqueuerService', () => {    
  // Recreate app before each test to clean the mock.
  beforeEach(async () => {
    mockQueue = {
      add: jest.fn(),
    };
    await createApp();
  })

  afterAll(async () => await module.close())

  describe('#foo', () => {
    it('adds a job', async () => {
      await enqueuerService.foo()

      // Check calls on the mock.
      expect(queue.add).toBeCalledTimes(1)
    })
  })

  describe('#bar', () => {
    // Override the mock for a specific test suite.
    beforeEach(async () => {
      mockQueue.add = jest.fn().mockImplementation(/** */);
      // The mock has changed so we need to recreate the app to use the new value.
      await createApp();
    })

    it('adds a job', async () => {
      // ...
    })
  })
})
于 2021-07-05T08:37:13.423 回答