typescript 53 lines · 7 steps

Unit testing a NestJS service with mocked providers

Wiring fake repositories and gateways into a NestJS test module to verify a service in isolation.

Explained by highlit
1import { Test, TestingModule } from '@nestjs/testing';
2import { getRepositoryToken } from '@nestjs/typeorm';
3import { Repository } from 'typeorm';
4 
5import { UsersService } from './users.service';
6import { PaymentGateway } from '../payments/payment.gateway';
7import { User } from './entities/user.entity';
8 
9describe('UsersService', () => {
10 let service: UsersService;
11 let paymentGateway: jest.Mocked<PaymentGateway>;
12 
13 const userRepository = {
14 findOne: jest.fn(),
15 save: jest.fn(),
16 create: jest.fn((dto) => dto),
17 };
18 
19 beforeEach(async () => {
20 const module: TestingModule = await Test.createTestingModule({
21 providers: [
22 UsersService,
23 {
24 provide: getRepositoryToken(User),
25 useValue: userRepository,
26 },
27 {
28 provide: PaymentGateway,
29 useValue: {
30 createCustomer: jest.fn().mockResolvedValue({ id: 'cus_123' }),
31 charge: jest.fn(),
32 },
33 },
34 ],
35 }).compile();
36 
37 service = module.get(UsersService);
38 paymentGateway = module.get(PaymentGateway);
39 });
40 
41 it('provisions a payment customer when registering', async () => {
42 userRepository.findOne.mockResolvedValue(null);
43 userRepository.save.mockImplementation(async (u: User) => ({ id: 1, ...u }));
44 
45 const user = await service.register({
46 email: 'ada@example.com',
47 password: 'hunter2',
48 });
49 
50 expect(paymentGateway.createCustomer).toHaveBeenCalledWith('ada@example.com');
51 expect(user.stripeCustomerId).toBe('cus_123');
52 });
53});
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Overriding providers with useValue lets you swap real dependencies for controllable test doubles.
  2. 2getRepositoryToken resolves the injection token TypeORM uses so you can mock a repository cleanly.
  3. 3Asserting on mocked collaborators verifies a service's side effects without hitting a real database or API.

Related explainers

typescript
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';

How a JWT strategy authenticates in NestJS

authentication jwt passport
Intermediate 7 steps
typescript
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, filter, take, switchMap } from 'rxjs/operators';

Refreshing auth tokens in an Angular interceptor

http-interceptor token-refresh rxjs
Advanced 8 steps
typescript
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';

How a GraphQL auth guard works in NestJS

authentication authorization jwt
Intermediate 7 steps
typescript
type ResizeCallback = (size: { width: number; height: number }) => void;
 
export function observeResize(callback: ResizeCallback, delay = 150) {
  let timeoutId: ReturnType<typeof setTimeout> | undefined;

Debouncing window resize in TypeScript

debounce closures event-listeners
Intermediate 6 steps
typescript
import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import { DataSource } from 'typeorm';

Graceful shutdown hooks in NestJS

graceful-shutdown lifecycle-hooks dependency-injection
Intermediate 7 steps
typescript
import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Cron, CronExpression } from '@nestjs/schedule';
 

A self-refreshing feature flags provider in NestJS

caching scheduled-tasks dependency-injection
Intermediate 8 steps

Share this explainer

Here's the card — post it anywhere.

Unit testing a NestJS service with mocked providers — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code