typescript - Hvordan kan en Nest Bull-kø testes via Jest (DI via @InjectQueue)?
Gitt en Injectable som bruker en kø via @InjectQueue-dekoratøren:
@Injectable()
export class EnqueuerService {
constructor (
@InjectQueue(QUEUE_NAME) private readonly queue: Queue
) {
}
async foo () {
return this.queue.add('job')
}
}
Hvordan kan jeg teste at denne tjenesten kaller køen riktig? Jeg kan lage det grunnleggende oppsettet:
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?)
})
})
})
Jeg er ganske fortapt når det gjelder oppsettet av Nest DI-beholderen, men jeg mistenker at det finnes en smart måte å gjøre dette på. Men til tross for timer med forsøk klarer jeg ikke å gjøre fremgang, og dokumentasjonen hjelper meg ikke. Kan noen tilby en løsning? Det må ikke være mocking, hvis det er enklere å lage en ekte kø for å teste mot, er det også greit, jeg vil bare verifisere at tjenesten min legger i kø som forventet! All hjelp tas imot med takk.
Svar #1
Ved å se i biblioteket, fant jeg en hjelpefunksjon som oppretter køinnsprøytings-token basert på navnet sitt: getQueueToken(name?: string).
Funksjonen eksporteres fra biblioteket slik at du kan bruke den til å levere din egen køimplementering for testing.
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)
})
})
})
REDIGER
For å sjekke metodene som kalles på køtjenesten, kan du lage en mock ved roten av testfilen din. Jeg reorganiserte testfilen for å ha en renere separasjon mellom oppsettet av testen og selve testen:
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(mockQueue.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 () => {
// ...
})
})
})
