Search code examples
testingmockingnestjsbullmq

Mocking Bull queues in NestJS


I am trying to test that after sending a request to one of my controllers, the queue pushes a job. The implementation itself works as expected.

This is my app.module.ts

        @Module({
        imports: [
        HttpModule,
        TypeOrmModule.forRoot(typeOrmConfig),
        BullModule.forRoot({
          redis: {
            host: redisConfig.host,
            port: redisConfig.port,
          },
        }),
        // Bunch of unrelated modules
         ],
         providers: [
        {
          provide: APP_FILTER,
          useClass: AllExceptionsFilter,
        },
        ],
         controllers: [SomeControllers],
        })
        export class AppModule {}

And this is how my import.module.ts (module using queues) looks like:

    @Module({
      imports: [
        BullModule.registerQueue({
          name: importQueueName.value,
        }),
       //More unrelated modules,
      ],
      providers: [
        //More services, and bull consumer and producer,
        ImportDataProducer,
        ImportDataConsumer,
        ImportDataService,
      ],
      controllers: [ImportDataController],
    })
    export class ImportDataModule {}

I tried to follow this approach

Which does not register the queue in the beforeAll hook, and I'm getting

     Driver not Connected

And this approach

Which registers a queue in the beforeAll hook in the test suite, and I am getting:

     TypeError: Cannot read properties of undefined (reading 'call')
    
          at BullExplorer.handleProcessor (node_modules/@nestjs/bull/dist/bull.explorer.js:95:23)
          at MapIterator.iteratee (node_modules/@nestjs/bull/dist/bull.explorer.js:59:26)
          at MapIterator.next (node_modules/iterare/src/map.ts:9:39)
          at FilterIterator.next (node_modules/iterare/src/filter.ts:11:34)
          at IteratorWithOperators.next (node_modules/iterare/src/iterate.ts:19:28)
              at Function.from (<anonymous>)
          at IteratorWithOperators.toArray (node_modules/iterare/src/iterate.ts:227:22)
          at MetadataScanner.scanFromPrototype (node_modules/@nestjs/core/metadata-scanner.js:12:14)
          at node_modules/@nestjs/bull/dist/bull.explorer.js:56:34
              at Array.forEach (<anonymous>)

This is my 'base test suite':

    describe('Queue test suite', () => {
      let app: INestApplication;
      const importQueue: any = { add: jest.fn() };
      beforeAll(async () => {
        const moduleFixture: TestingModule = await Test.createTestingModule({
          imports: [AppModule, ImportDataModule],
        })
          .overrideProvider(importQueueName.value)
          .useValue(importQueue)
          .compile();
    
        app = moduleFixture.createNestApplication();
        app.useGlobalPipes(
          new ValidationPipe({
            transform: true,
            whitelist: true,
            forbidNonWhitelisted: true,
          }),
        );
        await app.init();
      });
    
      afterAll(async () => {
       
        await app.close();
     
      });
    
      test('A job should be pushed', async () => {
        await request(app.getHttpServer())
          .post('/some/route')
          .attach('file', __dirname + '/some.file')
          .expect(HttpStatus.CREATED);
       
    
        expect(importQueue.add).toHaveBeenCalled();
      });
    });

Any idea what could be wrong here?


Solution

  • I had the same issue, the problem is with your mockQueue. You need to add a process mocked function.

    This should work for you!

    const importQueue: any = { 
      add: jest.fn(),
      process: jest.fn(),
    };
    

    And this is how I tested it.

    expect(mockQueue.add).toBeCalledTimes(1);
          expect(mockQueue.add).nthCalledWith(
            1,
            PendoJobNames.SCR,
            {
              ...mockJobDto,
            },
            {
              jobId: mockDto.visitorId,
              removeOnComplete: true,
              removeOnFail: true,
            },
          );
        ```