Exploring the realm of unit testing in the NestJS CQRS architecture journey

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:

  1. The inventory handler publishes an InventoryEvent
  2. 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()
   })
 })
});

Answer №1

To activate the functionality of eventBus and commandBus, you must initialize your application. Without a running application, these features will not work as expected.

describe('InventorySaga', () => {
  let saga: InventorySaga;
  let eventBus: EventBus
  let app: INestApplication;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        InventorySaga,
        EventBus,
        CommandBus
      ],
     }).compile();

     saga = module.get<InventorySaga>(InventorySaga);
     eventBus = module.get<EventBus>(EventBus);
    

     app = moduleRef.createNestApplication<INestApplication>();

     await app.init();
   });

   afterEach(async () => {
     if (app) {
       await app.close();
     }
   });

 describe('saga', () => {//stuff})
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Hmm, seems like there's an issue with the touchable child - it must either be native or forward setNativeProps to

Currently enrolled in a ReactNative course on Coursera, I am facing an error in a 4-year-old course: "Touchable child must either be native or forward setNativeProps to a native component." I am unsure about this error and would greatly appreciate any hel ...

Unable to simulate modules in jest using ESM in node version 18.12.1

Having trouble with mocking modules using jest in nodejs v18.12.1 with ESM. Following the standard example provided in jest documentation. module testing //user.js import axios from 'axios'; class Users { static all() { return axi ...

Is it possible to leverage Create-React-App's npm start in conjunction with Node.js?

I've recently started diving into React and node.js. My goal is to have a node.js server that can serve up React.js pages/views. After running 'create-react-app' and then using 'npm start', I'm not sure if I also need to man ...

Concealing axis lines within the initial circular grid or opting not to include them

Is there a way to incorporate some whitespace within the center circle of the radar chart? I'm aiming for the axis to commence at 1 radius (the initial circular line) or perhaps have the stoke set to 0 for the primary radius. Any assistance would be g ...

React Intersection Observer not functioning properly

Hey there! I'm trying to create an animation where the title slides down and the left element slides to the right when scrolling, using the intersection observer. Everything seems to be fine in my code, but for some reason it's not working. Any t ...

Here's the step-by-step process: Access the specific item in the object by referencing `obj[i]["name of desired attribute"]

I tried seeking advice and consulting multiple sources but none provided a suitable answer. Is there someone out there who can assist me? obj[i].["name of thing in object"] Here's the array: [ { "name": "DISBOARD#2760" ...

Create a full-width slider using Materialize CSS framework

When using materializecss to create a slider, I encountered an issue where the image is full width but not full height, causing scrollbars to appear. What changes can I make to ensure the slider fills out my screen without any scrollbars? Additionally, I ...

Looking for a JavaScript library to display 3D models

I am looking for a JavaScript library that can create 3D geometric shapes and display them within a div. Ideally, I would like the ability to export the shapes as jpg files or similar. Take a look at this example of a 3D cube: 3d cube ...

Encountering errors while running Angular 8's ng build prod command

After successfully migrating my project from Angular 7 to Angular 8, I encountered an issue when attempting to run 'ng build prod' which resulted in the following error: ERROR in Error during template compile of 'Ng2CompleterModule' Cou ...

Bug in toFixed causing incorrect results

function calculateTaxAndTotalRent(rent) { var phoneCharges = parseFloat($('#phone_charges').val()); phoneCharges = phoneCharges.toFixed(2); rent = parseFloat(rent); rent = rent.toFixed(2); var tax = parseFloat((rent * 15) / 1 ...

difficulty encountered during json parsing

I need help accessing displayName in req When I use this code snippet: console.log(req.session.passport.user._raw) The following information is displayed: { "kind": "plus#person", "etag": "\"ucaTEV-ZanNH5M3SCxYRM0QRw2Y/XiR7kPThRbzcIw-YLiAR ...

Infinite scroll layout meets Semantic UI visibility for a dynamic user experience

I am attempting to implement an infinite scrolling Masonry layout within the Semantic UI framework, utilizing the pre-existing visibility function. While everything appears to be functioning correctly, I am encountering difficulties with getting Masonry t ...

The text content is not in alignment with the server-rendered HTML for translation purposes with i18n

I have successfully implemented i18n in my Next.js project. The folder structure for my locales is as follows: public/locales/en/translation.json and public/locales/fr/translation.json The error I am encountering is: Uncaught Error: Text content does n ...

Issue with resetting input forms in ReactJS

I have a simple form that seems to be clearing the rest of the fields every time I try to fill it. Additionally, validation errors are being displayed below each field instead of just one. How can this issue be fixed? Here is how the form looks before any ...

Implementing Node Express 4 to efficiently handle response generation from successive API requests

I'm currently in the process of developing a NodeJS server using Express4. The main purpose of this server is to act as a mediator between my frontend Angular app and a 3rd party API. I've set up a specific path that my frontend app can request, ...

Harnessing the power of the bluebird promise library

Within myPeople function, there is a promise function being called as shown below: var myPeople = function(){ var go; return new Promise (function(resolve){ User .getPeople() .then(function(allPeople){ ...

Secure an input field for exclusive attention. React

How can I lock the focus of my input field? I attempted using the following code: onBlur={this.click()} However, it was not successful. What is the correct way to accomplish this? ...

Angular directive specifically meant for the parent element

I am working on a directive that I need to apply to a specific div element without affecting its child elements. The goal is to make the main div draggable, so that when it moves, its child divs move along with it. However, I do not want the child divs to ...

Angular - a simple method to determine the number of non-empty inputs in a complex template-driven form

As I work on multiple substantial Angular 11 template forms containing basic inputs like text, radiolists, and checkboxes, I am looking for the most effective method to calculate the percentage of completed inputs while the user is actively engaging with ...

The ASP.NET MVC3 form collection registers as 0 when performing a jQuery ajax post

I'm currently developing a project on ASP.NET MVC3. My current challenge involves POSTing to a method that should return a set of data using the jQuery.ajax api. However, upon handling the request on the server, I noticed that the form collection&apo ...