When conducting tests in Nest.js, it appears that the dependencies of ReportingService, specifically MasterMLRepository, cannot be resolved. This issue is likely due to Sequelize not being available in

As I work on writing tests in Nest.js, I encountered an error that has been quite challenging to resolve. Despite trying various solutions found online, such as adding Sequelize to different sections like imports, exports, etc., none of them seemed to work.

Nest can't resolve dependencies of the ReportingService (?, MasterMLRepository). Please make sure that the argument Sequelize at index [0] is available in the RootTestModule context.

    Potential solutions:
    - Is RootTestModule a valid NestJS module?
    - If Sequelize is a provider, is it part of the current RootTestModule?
    - If Sequelize is exported from a separate @Module, is that module imported within RootTestModule?
      @Module({
        imports: [ /* the Module containing Sequelize */ ]
      })

Please check out the files below:

reporting.controller.ts

import { Controller, Get, Param } from '@nestjs/common';
import { PinoLogger } from 'nestjs-pino';
import { MasterML } from './reporting.model';
import { ReportingService } from './reporting.service';

Controller()
export class ReportingController {
  constructor(
    private readonly reportingService: ReportingService,
    private readonly logger: PinoLogger,
  ) {
    this.logger.setContext(ReportingController.name);
  }

  @Get()
  async getMaterialIds(
    @Param('plantId') plantId: string,
  ): Promise<string[] | []> {
    try {
      return await this.reportingService.getMaterialIds(plantId);
    } catch (error) {
      this.logger.error(error);
      throw error;
    }
  }
}

reporting.model.ts

import { Column, Model, Table } from 'sequelize-typescript';

@Table({ tableName: 'MASTER_ML' })
export class MasterML extends Model {
  @Column({ field: 'BATCH_ID' })
  batchId: string;

  @Column({ field: 'FEATURE_NAME' })
  featureName: string;

  @Column({ field: 'FEATURE_VALUE' })
  featureValue: string;

  @Column({ field: 'MATERIAL_ID' })
  materialId: string;

  @Column({ field: 'PLANT_ID' })
  plantId: string;

  @Column({ field: 'STAGE' })
  stage: string;

  @Column({ field: 'MANUFACTURE_DATE' })
  manufactureDate: Date;
}

reporting.module.ts

import { Module } from '@nestjs/common';
import { SequelizeModule } from '@nestjs/sequelize';
import { Sequelize } from 'sequelize-typescript';
import { ReportingController } from './reporting.controller';
import { ReportingService } from './reporting.service';
import { MasterML } from './reporting.model';

@Module({
  imports: [SequelizeModule.forFeature([
    MasterML,
  ])],
  controllers: [ReportingController],
  providers: [ReportingService],
})
export class ReportingModule {}

reporting.service.ts

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { Sequelize } from 'sequelize-typescript';
import { MasterML } from './reporting.model';

@Injectable()
export class ReportingService {
  materialIdAttributes: any[] = [
    [
      this.sequelize.fn('DISTINCT', this.sequelize.col('MATERIAL_ID')),
      'materialId',
    ],
  ];

  materialIdOrder: any[] = [[this.sequelize.literal("'materialId' ASC")]];

  constructor(
    public sequelize: Sequelize,
    @InjectModel(MasterML) private readonly masterMl: typeof MasterML,
  ) {}

  async getMaterialIds(
    plantId: string,
  ): Promise<string[] | []> {
    return this.masterMl.findAll({
      attributes: this.materialIdAttributes,
      where: { plantId },
      order: this.materialIdOrder,
    }).then((rows) => rows.map((row) => row.materialId));
  }
}

reporting.service.spec.ts

import { getModelToken } from '@nestjs/sequelize';
import { Test } from '@nestjs/testing';
import { MasterML } from '../../../src/simply/reporting/reporting.model';
import { ReportingService } from '../../../src/simply/reporting/reporting.service';

const testMaterialIds: string[] = [
  'MTRL-1',
  'MTRL-2',
  'MTRL-3',
];

describe('ReportingService', () => {
  let service: ReportingService;
  let masterMlModel: typeof MasterML;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [
        ReportingService,
        {
          provide: getModelToken(MasterML),
          useValue: {
            findAll: jest.fn(() => [testMaterialIds]),
          },
        },
      ],
    }).compile();
    service = module.get(ReportingService);
    masterMlModel = module.get<typeof MasterML>(getModelToken(MasterML));
  });

  it('Service should be defined', () => {
    expect(service).toBeDefined();
  });

  it('getMaterialIds - should be defined', async () => {
    expect(await service.getMaterialIds('PLNT1')).toBeDefined();
  });

  it('should get the getMaterialIds', async () => {
    expect(await service.getMaterialIds('PLNT1')).toEqual([testMaterialIds]);
    const findAllSpy = jest.spyOn(masterMlModel, 'findAll');
    expect(findAllSpy).toHaveBeenCalled();
  });
});

simply.module.ts

import { Module } from '@nestjs/common';
import { Routes, RouterModule } from '@nestjs/core';
import { Sequelize } from 'sequelize-typescript';
import { PlantModule } from './plant/plant.module';
import { Plant } from './plant/plant.model';
import { ProductModule } from './product/product.module';
import { ProcessModule } from './process/process.module';
import { ReportingModule } from './reporting/reporting.module';
import { Product } from './product/product.model';
import { Process } from './process/process.model';
import { MasterML } from './reporting/reporting.model';

const routes: Routes = [
  {
    path: '/plants',
    module: PlantModule,
    children: [
      {
        path: '/:plantId/products',
        module: ProductModule,
        children: [
          {
            path: '/:productId/processes',
            module: ProcessModule,
          },
        ],
      },
      {
        path: '/:plantId/reporting',
        children: [
          {
            path: '/materialids',
            module: ReportingModule,
          },
        ],
      },
    ],
  },
];
@Module({
  imports: [
    RouterModule.register(routes),
    PlantModule,
    ProductModule,
    ProcessModule,
    ReportingModule,
  ],
})
export class SimplyModule {
  constructor(public sequelize: Sequelize) {
    this.sequelize.addModels([
      Plant,
      Product,
      Process,
      MasterML,
    ]);
  }
}

app.module.ts

import { Module } from '@nestjs/common';
import { LoggerModule, Params } from 'nestjs-pino';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TerminusModule } from '@nestjs/terminus';
import { SequelizeModuleOptions, SequelizeModule } from '@nestjs/sequelize';
import {
  apiConfig, logConfig, sequelizeConfig,
} from './config';

import JoiSchema from './config/schema/validation.schema';
import { AuthModule } from './auth/auth.module';
import { TerminusController } from './terminus/terminus.controller';
import { SimplyModule } from './simply/simply.module';

const imports = [
  TerminusModule,
  ConfigModule.forRoot({
    validationSchema: JoiSchema,
    validationOptions: {
      allowUnknown: true,
      abortEarly: false,
    },
    isGlobal: true,
    envFilePath: process.env.NODE_ENV ? `env/.env.${process.env.NODE_ENV}` : 'env/.env.local',
    load: [
      apiConfig,
      logConfig,
      sequelizeConfig,
    ],
  }),

  LoggerModule.forRootAsync({
    useFactory: async (configService: ConfigService) =>
      configService.get<Params>('log'),
    inject: [ConfigService],
  }),
  SequelizeModule.forRootAsync({
    useFactory: (configService: ConfigService) =>
      configService.get<SequelizeModuleOptions>('sequelize'),
    inject: [ConfigService],
  }),
  AuthModule,
  SimplyModule,
];

@Module({
  imports,
  controllers: [TerminusController],
  providers: [],
})
export class AppModule { }

Answer №1

I decided to put Sequelize and its functions to the test -

beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [
        ProcessReportingService,
        {
          provide: Sequelize,
          useValue: {
            fn: jest.fn,
            col: jest.fn,
            literal: jest.fn,
            where: jest.fn,
          },
        },
        {
          provide: getModelToken(MasterML),
          useValue: {
            findAll: jest.fn(() => new Promise((resolve) => {
              resolve(testRawMaterialIds);
            })),
          },
        },
        {
          provide: getModelToken(Int2Calculations),
          useValue: {
            findAll: jest.fn(() => new Promise((resolve) => {
              resolve(testInt2CalculationsRows);
            })),
          },
        },
      ],
    }).compile();
    service = module.get(ProcessReportingService);
    masterMlModel = module.get<typeof MasterML>(getModelToken(MasterML));
    int2CalculationsModel = module.get<typeof Int2Calculations>(getModelToken(Int2Calculations));
  });

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 modifying an observable

Previously, I utilized Firebase as my database service. When I received the Observable from Firebase and subscribed to it: const projectsFromDatabase = this.afDB.list(this.basePath, { query: query }); console.log(projectsFromDatabase.subscribe(res =&g ...

Retrieve information prior to CanActivation being invoked

As I develop a web application utilizing REST to retrieve data (using Spring Boot), the server employs cookies for authenticating logged-in users. Upon user signing in, I store their information in the AuthenticationHolderService service (located in root ...

What could be causing my Vue code to behave differently than anticipated?

There are a pair of components within the div. When both components are rendered together, clicking the button switches properly. However, when only one component is rendered, the switch behaves abnormally. Below is the code snippet: Base.vue <templa ...

Utilizing generic type and union types effectively in TypeScript

When initializing my class, I often pass in either a value or an object containing information about the value. For instance: new Field<string>('test') new Field<string>({value: 'test', inactive: 'preview'}) Howev ...

Get rid of the strange border on the material dialog

I am struggling with the Angular material 6 dialog component as it is displaying a strange border. I have attempted to remove it using the code below, without success. Interestingly, when I apply the style inline in the browser, it works fine. Any suggesti ...

Ways to specify an unused parameter within a function

As I work on my code, I encounter the need to separate the key and value from a request params object in order to validate the value using ObjectID. To achieve this, I decided to iterate over an array of entries and destructure the key and value for testin ...

Tips for managing numerous HTTP requests in Angular 6

I have a method that is trying to chain together 3 requests like this: showProfileDetails() { this.getUserInfo(this.currentUser.id).pipe( mergeMap(e => this.getAccounts(this.currentUser.id) ), mergeMap(e => this.getPayments ...

Struggling to securely post data to an Express server by hashing passwords with bcrypt?

I'm currently working on developing an API using Express and Sequelize. Specifically, I am writing a function to create a new user where I utilize bcrypt for password hashing. const createNewUser = (data) => { return new Promise(async (resolve, ...

The API request does not provide randomized results and does not show any display

I am facing an issue with a button that is supposed to fetch a random user from an API. When I retrieve all the users, the information is displayed correctly. However, when I try to select a user at random, it does not work as expected. Also, it seems to a ...

Tips for incorporating moment.js library into an Angular 2 TypeScript application

I attempted to utilize TypeScript bindings in my project: npm install moment --save typings install moment --ambient -- save test.ts file content: import {moment} from 'moment/moment'; I also tried without using TypeScript bindings: npm inst ...

Ways to initiate a fresh API request while utilizing httpClient and shareReplay

I have implemented a configuration to share the replay of my httpClient request among multiple components. Here is the setup: apicaller.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http& ...

Capture and handle JavaScript errors within iframes specified with the srcDoc attribute

My current project involves creating a React component that can render any HTML/JavaScript content within an iframe using the srcDoc attribute. The challenge I am facing is implementing an error handling system to display a message instead of the iframe ...

There seems to be an issue with the Angular QuickStart project as it is not functioning properly, showing the error message "(

After following the instructions in this guide for setting up VS2015, I encountered issues when trying to run the "quick start" project or the "tour of heroes" tutorial on Google Chrome. The error message I received can be found here: Angular_QuickStart_Er ...

Unlocking the power of vscode webview with acquireVsCodeApi in a React project

Currently, I am in the process of creating a new extension for Visual Studio Code using Webview. However, I am encountering difficulties with sending messages from the client to the extension. To kickstart the project, I decided to utilize this awesome rep ...

Building a CRUD application with Node.js and Sequelize ORM

In my quest for a straightforward illustration of how to implement CRUD code using the most up-to-date version 4.15 of Sequelize ORM, it was disheartening that I couldn't locate an all-inclusive solution. Consequently, I had to conduct individual sear ...

Tips for patiently anticipating the completed response within an interceptor

Using the interceptor, I want to display a loading spinner while waiting for subscriptions to complete. This approach works well in individual components by showing and hiding the spinner accordingly: ngOnInit() { this.spinnerService.show(); this. ...

Encountering unexpected errors with Typescript while trying to implement a simple @click event in Nuxt 3 projects

Encountering an error when utilizing @click in Nuxt3 with Typescript Issue: Type '($event: any) => void' is not compatible with type 'MouseEvent'.ts(2322) __VLS_types.ts(107, 56): The expected type is specified in the property ' ...

Guide on utilizing interface within a class for functional de-structuring in TypeScript

In my current project, I have a Class that contains a method which I would like to utilize in a de-structured manner, primarily due to the presence of multiple optional arguments. To maintain strict typing, I have created an Interface to define these argum ...

What is the method for retrieving the name of an object's property within an Angular template

I am trying to display the name of a nested object's property using Angular interpolation <ng-container ngFor="let item of saleDetailsAggegater.productMap | keyvalue"> <tr *ngFor="let qtyMap of item.value | keyvalue"&g ...

Encountering an undefined property error while using Array.filter in Angular 2

hello everyone, I am currently faced with an issue while working on a project that involves filtering JSON data. When using the developer tools in Chrome, it keeps showing me an error related to undefined property. chart: JsonChart[] = []; charts: JsonC ...