We're currently working on writing unit tests using Jest for our application and are facing difficulties in testing sagas. Specifically, we're having trouble testing the saga itself.
During our unit testing process, we've encountered an issue where when we publish an InventoryEvent in the inventory.saga.spec.ts file and expect it to be received by the inventory() function in inventory.saga.ts, the event is not being received by the saga. However, when the same event is published through the application, it is successfully received by the saga.
We need help identifying why the event published through the test file is not reaching the saga.
The current flow of the application is as follows:
- The inventory handler publishes an InventoryEvent
- The saga acts as an event listener, listening for the InventoryEvent and invoking the InventoryCacheCommand
Below are snippets of the code:
inventory.handler.ts
await this.eventBus.publish(new InventoryEvent(inventoryData));
inventory.event.ts
import { IEvent } from '@nestjs/cqrs';
import { InventoryStatusInterface } from '../../../interface/inventory.interface';
export class InventoryEvent implements IEvent {
constructor(public readonly inventoryData: InventoryStatusInterface) {}
}
inventory.saga.ts
import { Injectable } from '@nestjs/common';
import { ICommand, ofType, Saga } from '@nestjs/cqrs';
import { map, Observable } from 'rxjs'
import { createLog } from '../../infrastructure/service/utils/logger';
import { InventoryCacheCommand } from '../commands/impl/inventory-cache.command';
import { InventoryEvent } from '../events/impl/inventory.event';
@Injectable()
export class InventorySaga {
private logger = createLog(InventorySaga.name);
@Saga()
inventory = (events$: Observable<any>): Observable<ICommand> => {
return events$.pipe(
ofType(InventoryEvent),
map((event: InventoryEvent) => {
this.logger.info('received inventory event to upsert inventory cache: ', event.inventoryData);
return new InventoryCacheCommand(event.inventoryData);
})
);
}
}
inventory.saga.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { InventorySaga } from './inventory.saga';
import { InventoryEvent } from '../events/impl/inventory.event';
import { CommandBus, EventBus } from '@nestjs/cqrs';
import { InventoryCacheCommand } from '../commands/impl/inventory-cache.command';
import { Observable } from 'rxjs';
jest.mock('../commands/impl/inventory-cache.command')
describe('InventorySaga', () => {
let saga: InventorySaga;
let eventBus: EventBus
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
InventorySaga,
EventBus,
CommandBus
],
}).compile();
saga = module.get<InventorySaga>(InventorySaga);
eventBus = module.get<EventBus>(EventBus)
});
describe('saga', () => {
it('should publish InventoryEvent', async () => {
const inventoryData = [
{
sku: 'TH4344-43-L',
qty: 3,
},
{
sku: 'TH4344-43-S',
qty: 55,
},
{
sku: 'TH4344-43-XL',
qty: 55,
},
];
const spy = jest.spyOn(saga, 'inventory');
await eventBus.publish(new InventoryEvent(inventoryData));
expect(spy).toBeCalled()
})
})
});