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

Tips for turning on a gaming controller before using it

Current Situation In my ionic side menu app, I have a main controller called 'main view'. Each tab in the app has its own controller, which is a child of the main controller. The issue I'm facing is that when I start the app, the first cont ...

Placing a div tag directly beneath another div tag

Imagine you have a div element with the class name divstudent, let's call this one div1. Now, you want to dynamically create another div element below div1, but outside of it using JavaScript. How can you achieve this? "div1" <div class="divstuden ...

Working with MySQL in Node.js using async/await

Struggling with utilizing async/await in Node.js with MySQL as it consistently returns an undefined value. Can someone shed light on what could be causing this issue? See my code snippet below. const mysql = require('promise-mysql'); var co ...

The useContext hook was utilized in conjunction with useReducer, however, a child component is unexpectedly showing an

First and foremost, I want to express my gratitude for your unwavering support. As a newcomer to the realm of ReactJS, I am currently navigating through the development of a concept example for a product store that includes various filters in the form of ...

Guide on uploading images to a NodeJS server from an Angular 2+ application

I have a NodeJS rest-API that can handle both images and text content in the same function. Currently, I am using multer in my NodeJS server to manage image uploads along with text content. Below is an example code snippet showcasing how I am handling this ...

I aim to customize the options of a dropdown list based on the selection of another dropdown

I am looking for a way to dynamically filter employees based on the department selected. Unfortunately, my knowledge of JavaScript and Ajax is limited. <div class="pure-checkbox ml-15"> <input id="checkbox2" name="sta ...

Material-UI Swipeable Drawer with a see-through design

Any tips on how to apply a 'transparent' style background property to my SwipeableDrawer component from Material UI? I'm having difficulty changing the background directly from my code since the component generates another component in the H ...

What advantages does Angular Service offer when gathering information compared to utilizing only $http?

Comparing Two Approaches: Approach A. Creating app module Using a service to store model data Implementing a controller to retrieve data from the service File 1: Users.js: angular.module('users', []); File 2: userService.js: angular ...

Unable to apply inline styles to React Component

My Carousel component is supposed to return a collection of carousel boxes, each styled with a specific property. However, I am facing an issue where the style property is not being applied to the returning divs. How can I resolve this? I noticed that whe ...

The redirect function is failing to carry the "req" parameter

Express Routes Troubleshooting app.get('/auth/google/redirect', passport.authenticate('google'), (req, res) => { console.log('req.user:', req.user) //>>>>>Outputs {username: 'bob', id: '.. ...

Unable to fetch data from Array using an identifier in Angular 4

My goal is to fetch an object from an array by its id using the getItem() method. import { Injectable } from '@angular/core'; import { Http, Response } from '@angular/http'; import { Observable } from 'rxjs/Observable'; impor ...

Why does the fillText() method in HTML5 Canvas erase everything after using the clearRect() method?

Whenever I use the writeTextToCanvas method before the clearCanvas method, everything works perfectly. However, if I call the clearCanvas method first and then writeTextToCanvas, the drawing functions work fine after clearing the canvas but the fillText fu ...

JavaScript and jQuery are lightning fast, especially when the page is reloaded

I am currently working on a responsive website that uses liquid layouts. I have encountered challenges when incorporating images in the design, especially when dealing with different browsers like IE, Firefox, and Chrome. Recently, I faced another issue w ...

Tips for modifying a particular element within a class?

I am seeking guidance on how to modify a specific class attribute in CSS/JS/JQuery when hovering over a different element. Here is an example: <h1 class="result">Lorium</h1> <h1 class="result">Ipsum</h1> <h1 class="result">Do ...

Encountered a "Transformer is not a constructor" error when trying to upgrade the React Native SDK version from 0.61.5 to 0.64

https://i.sstatic.net/LYdxj.pngRecently, I upgraded my react native version to the latest one and encountered an error stating "Transformer is not a constructor". The metro-react-native-babel-preset version I am currently using is 0.64.0. Can someone ple ...

Using AngularJS, generate a JSON array with a specified key

Looking to create a JSON array structure with keys using AngularJS, but unsure how to push data in order to achieve this. The goal is to generate a JSON array based on the provided data below. $scope.category = [{"id": 20, "name": "vegetable"}, {"id": ...

Updating variable values in AngularJS while navigating through routes

How can I dynamically set the flag icon inside the page header based on the selected language using AngularJS? The language selection is done in a separate .htm file and all managed by AngularJS routing. My application has a single controller called "appCo ...

How can I retrieve the children of a component in React?

Currently, I am working on implementing Class Components for a project involving a main picture and a smaller pictures gallery stored in an array. The overall structure consists of an all pictures container that houses both the main picture and smaller pic ...

Calculate the cumulative values in ng-repeat using AngularJS

I used ng-repeat to iterate through a JSON array. I calculated the number of nights by utilizing the dayDiff() function. Now, I need to find the total number of nights for all invoices. My project is built with AngularJS. Is there a way to retrieve the to ...

Having trouble getting the Pokemon modal to show both the type and image?

HTML: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>My First JS App</title> <lin ...