Ever since adding a mock CronJob to my unit tests, I'm getting the message below:
A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks. Active timers can also cause this, ensure that .unref() was called on them.
Running the test with --detectOpenHandles doesn't give me any extra information. How do I exit this test gracefully?
This is the test code:
import { Test, TestingModule } from '@nestjs/testing';
import { CronService } from './cron.service';
import { SchedulerRegistry } from '@nestjs/schedule';
import { CronJob } from 'cron';
import { PriceModule } from '../price/price.module';
describe('CronService', () => {
let service: CronService;
let mockCronJob: CronJob = new CronJob('*/5 * * * *', () => {
return;
});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
CronService,
{
provide: SchedulerRegistry,
useValue: {
addCronJob: jest.fn().mockResolvedValue(mockCronJob),
}
}
],
imports: [PriceModule]
}).compile();
service = module.get<CronService>(CronService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
With the actual service being tested:
import { Injectable, Logger } from '@nestjs/common';
import { SchedulerRegistry } from '@nestjs/schedule';
import { CronJob } from 'cron';
import { PriceService } from '../price/price.service';
@Injectable()
export class CronService {
constructor(private schedulerRegistry: SchedulerRegistry, private priceService: PriceService) {
this.addCronJob('startup',1)
}
private readonly logger = new Logger(CronService.name);
addCronJob(name: string, minutes: number): number {
let priceUpdateTimer = 'priceUpdateTimer'
if(0 >= minutes || minutes >= 60) {
this.logger.error(`Tried updating the priceUpdatetimer to ${minutes} minutes.`);
return;
}
if(name === priceUpdateTimer || name == 'startup') {
if(name === priceUpdateTimer && this.getCronByName(priceUpdateTimer)) {
this.deleteCron(priceUpdateTimer);
}
let cronString = `*/${minutes} * * * *`;
const priceUpdateJob = new CronJob(cronString, () => {
this.logger.log('Updating prices.')
this.priceService.updatePrices();
});
this.schedulerRegistry.addCronJob(priceUpdateTimer, priceUpdateJob);
priceUpdateJob.start();
this.logger.log(`Changed the priceUpdatetimer to ${minutes} minutes.`);
return minutes;
}
}
deleteCron(name: string) {
this.schedulerRegistry.deleteCronJob(name);
this.logger.warn(`job ${name} deleted!`);
}
getCronByName(name: string): number {
try {
const job = this.schedulerRegistry.getCronJob(name);
let minutes: number = Number.parseInt(job.cronTime.source.toString().replace(/\D/g, ""));
return minutes;
} catch {
this.logger.warn(`No cron job found by name ${name}`);
return;
}
}
}