Search code examples
unit-testingjestjsnestjs

Nestjs cannot resolve dependencies in service spec


I tried writing a nestjs project that just saves some objects in mongodb, but running the test specs (users.service and users.controller), I get this mysterious error:

Nest can't resolve dependencies of the UsersService (?). Please make sure that the argument "UserModel" at index [0] is available in the RootTestModule context.

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

   6 |
   7 |   beforeEach(async () => {
>  8 |     const module: TestingModule = await Test.createTestingModule({
     |                                   ^
   9 |       providers: [UsersService],
  10 |     }).compile();
  11 |

I have a root app module, that imports and sets up mongoose and a users module. The app controller has the / endpoint which just returns a status message. The tests for the app controller work. I am getting the above error for both user service and users controller specs. Here is the users service spec for example

import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';

describe('UsersService', () => {
  let service: UsersService;

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

    service = module.get<UsersService>(UsersService);
  });

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

The user controller has 2 endpoints to create and fetch a new user. Here is the user module:

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './schemas/user.schema';

@Module({
  imports: [
    MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
  ],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

Followed by the users service:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { User } from './schemas/user.schema';
import { Model } from 'mongoose';
import { CreateUserDto } from './dto/create-user.dto';

@Injectable()
export class UsersService {
  constructor(@InjectModel(User.name) private userModel: Model<User>) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = new this.userModel(createUserDto);
    return user.save();
  }

  async findById(id: string): Promise<User> {
    return this.userModel.findById(id).exec();
  }
}

And here is the users controller:

import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from './schemas/user.schema';

@Controller('users')
export class UsersController {
  constructor(private usersService: UsersService) {}

  @Post()
  async create(@Body() createUserDto: CreateUserDto) {
    await this.usersService.create(createUserDto);
  }

  @Get(':id')
  async findById(@Param('id') id: string): Promise<User> {
    return this.usersService.findById(id);
  }
}

The problem is in how the spec is written. I'm quite new to dependency injection frameworks so your help in understanding and solving this is much appreciated.


Solution

  • You need to add in mocks of the dependencies of the UsersService, in this case, a custom provider that matches the injection token of @InjectModel(User.name). Fortunately, @nestjs/mongoose has a helper method to create this token, so you only need to add a mock instance to your providers array of the testing module and you'll be good to go.

    import { getModelToken } from '@nestjs/mongoose';
    import { Test, TestingModule } from '@nestjs/testing';
    import { User } from './schemas/user.schema';
    import { UsersService } from './users.service';
    
    describe('UsersService', () => {
      let service: UsersService;
    
      beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
          providers: [
            UsersService,
            {
              provide: getModelToken(User.name),
              useValue: {
                findById: () => ({
                  exec: jest.fn().mockResolvedValue(someMockUserObject)
                })
              }
            }
          ],
        }).compile();
    
        service = module.get<UsersService>(UsersService);
      });
    
      it('should be defined', () => {
        expect(service).toBeDefined();
      });
    });
    

    You can see more in-depth examples in this community maintained testing example repository