How To Mock Repository, Service and Controller In NestJS (Typeorm & Jest)
Asked Answered
A

2

12

I'm new at typescript. My Nestjs project app is something like this. I'm trying to use repository pattern, so i separated business logic (service) and persistance logic (repository)

UserRepository

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

import { UserEntity } from './entities/user.entity';

@Injectable()
export class UserRepo {
  constructor(@InjectRepository(UserEntity) private readonly repo: Repository<UserEntity>) {}

  public find(): Promise<UserEntity[]> {
    return this.repo.find();
  }
}

UserService

import { Injectable } from '@nestjs/common';
import { UserRepo } from './user.repository';

@Injectable()
export class UserService {
  constructor(private readonly userRepo: UserRepo) {}

  public async get() {
   return this.userRepo.find();
  }
}

UserController

import { Controller, Get } from '@nestjs/common';

import { UserService } from './user.service';

@Controller('/users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  // others method //

  @Get()
  public async getUsers() {
    try {
      const payload = this.userService.get();
      return this.Ok(payload);
    } catch (err) {
      return this.InternalServerError(err);
    }
  }
}

How do i create unit testing for repository, service & controller without actually persist or retrieve data to DB (using mock)?

Amaranth answered 12/3, 2020 at 10:54 Comment(0)
N
19

Mocking in NestJS is pretty easily obtainable using the testing tools Nest exposes is @nestjs/testing. In short, you'll want to create a Custom Provider for the dependency you are looking to mock, and that's all there is. However, it's always better to see an example, so here is a possibility of a mock for the controller:

describe('UserController', () => {
  let controller: UserController;
  let service: UserService;
  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      controllers: [UserController],
      providers: [
        {
          provide: UserService,
          useValue: {
            get: jest.fn(() => mockUserEntity) // really it can be anything, but the closer to your actual logic the better
          }
        }
      ]
    }).compile();
    controller = moduleRef.get(UserController);
    service = moduleRef.get(UserService);
  });
});

And from there you can go on and write your tests. This is pretty much the same set up for all tests using Nest's DI system, the only thing to be aware of is things like @InjectRepository() and @InjectModel() (Mongoose and Sequilize decorators) where you'll need to use getRepositoryToken() or getModelToken() for the injection token. If you're looking for more exmaples take a look at this repository

Nicholnichola answered 12/3, 2020 at 15:28 Comment(3)
can you please provide a full example of this please? I am looking to achieve the same.Mello
The repository linked in my answer has full examples you can look at @MelloNicholnichola
I wasn't looking in the right place before. Found it now, thank you!Mello
S
0

The answer to this question is good, but I would like to complete it a bit. Since Nest.js v8, we have access to auto-mocking. A full article about it on Trilon's blog.

Installing @golevelup/ts-jest, we have access to a function called createMock that can be used as parameter of useMocker and so just like that we can mock (almost) everything.

An example below, but please go read that article I linked above as well:

import { createMock } from '@golevelup/ts-jest';

describe('UserController', () => {
  let controller: UserController;
  let service: UserService;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      controllers: [UserController],
    })
      .useMocker(createMock) // <-- Mock everything
      .compile();

    controller = moduleRef.get(UserController);
    service = moduleRef.get(UserService);
  });
});

Don't forget to clear your mocks:

afterEach(() => {
  jest.clearAllMocks();
});

As an example, to spy and/or redefine the returned or resolved value we can use:

service.findOne = jest.fn();
jest.spyOn(service, 'findOne').mockImplementation(async () => user);
expect(service.findOne).toHaveBeenCalled();
Socialism answered 4/7 at 13:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.