vvalerii
vvalerii

Reputation: 5

How to mock response from service for testing controller in typescript using Jest

I am testing a controller written in typescript using Jest. I try to mock the response of the service, but it does not work out.

this is my EmployeesController

import { EmployeesService } from '../services/employeeService';
import { IDBConnection } from '../config/IDBConnection';

export class EmployeesController {
  private employeeService: EmployeesService;

  constructor(dbConnection: IDBConnection) {
    this.employeeService = new EmployeesService(dbConnection);
  }
  public async findAllEmployees(req: any, res: any) {
    const numPerPage = +req.query.pagesize;
    const page = +req.query.page;
    try {
      const count = await this.employeeService.findCount();
      const results = await this.employeeService.findAll(numPerPage, page);
      let totalEmployee = count[0].totalCount;
      if (totalEmployee === 0) {
        return res.status(404).json({
          success: false,
          message: 'Employee not found'
        });
      } else if (count && results) {
        return res.status(200).json({
          employees: results,
          maxEmployees: totalEmployee
        });
      };
    } catch {
      res.status(500).json({
        success: false,
        message: 'Server error'
      });
    };
  }

this is my EmployeesService

import { IDBConnection } from '../config/IDBConnection';
export class EmployeesService {
  private connection: any;

  constructor(connection: IDBConnection) {
    this.connection = connection;
  }
  async findCount() {
    const results = await this.connection.execute('SELECT count(*) as totalCount FROM EmployeeDB.Employees');
    return results; // [ RowDataPacket { totalCount: 5 } ]
  }
}

I can assume I am piping to it incorrectly from my service in test but I am not too sure. Is anyone able to help me?

this is my Employee.test

jest.mock('../../../services/employeeService');

import { EmployeesController } from '../../../controllers/employeeController';
import { EmployeesService } from '../../../services/employeeService';

describe('Employees', () => {
   test('should get count of employees', async () => {
      const getCount = jest.spyOn(EmployeesService.prototype, "findCount")
         .mockImplementation(() => Promise.resolve([{totalCount: 5}]));
      const mockResp = () => {
         const res: any = {}
         res.status = jest.fn().mockReturnValue(res)
         res.json = jest.fn().mockReturnValue(res)
         return res
      }
      const mockReq = () => {
         const req: any = {}
         req.query = jest.fn().mockReturnValue(req);
         return req
      }
      const req = mockReq({
         pagesize: 1,
         page: 0
      });
      const res = mockResp();
      await EmployeesController.prototype.findAllEmployees(req, res);
      expect(getCount).toHaveBeenCalledTimes(1); // Received number of calls: 0
   }
}

Upvotes: 0

Views: 4745

Answers (1)

Lin Du
Lin Du

Reputation: 102457

Here is the unit test solution:

controllers/employeeController.ts:

import { EmployeesService } from '../services/employeeService';
import { IDBConnection } from '../config/IDBConnection';

export class EmployeesController {
  private employeeService: EmployeesService;

  constructor(dbConnection: IDBConnection) {
    this.employeeService = new EmployeesService(dbConnection);
  }
  public async findAllEmployees(req: any, res: any) {
    const numPerPage = +req.query.pagesize;
    const page = +req.query.page;
    try {
      const count = await this.employeeService.findCount();
      const results = await this.employeeService.findAll(numPerPage, page);
      let totalEmployee = count[0].totalCount;
      if (totalEmployee === 0) {
        return res.status(404).json({
          success: false,
          message: 'Employee not found',
        });
      } else if (count && results) {
        return res.status(200).json({
          employees: results,
          maxEmployees: totalEmployee,
        });
      }
    } catch {
      res.status(500).json({
        success: false,
        message: 'Server error',
      });
    }
  }
}

services/employeeService.ts:

import { IDBConnection } from '../config/IDBConnection';

export class EmployeesService {
  private connection: any;

  constructor(connection: IDBConnection) {
    this.connection = connection;
  }
  async findCount() {
    const results = await this.connection.execute('SELECT count(*) as totalCount FROM EmployeeDB.Employees');
    return results; // [ RowDataPacket { totalCount: 5 } ]
  }
  async findAll(numPerPage, page) {
    return [];
  }
}

config/IDBConnection.ts:

export interface IDBConnection {}

Employee.test.ts:

import { EmployeesController } from './controllers/employeeController';
import { EmployeesService } from './services/employeeService';

jest.mock('./services/employeeService', () => {
  const mEmployeesService = {
    findCount: jest.fn(),
    findAll: jest.fn(),
  };
  return { EmployeesService: jest.fn(() => mEmployeesService) };
});

describe('Employees', () => {
  afterEach(() => {
    jest.resetAllMocks();
  });
  test('should get count of employees', async () => {
    const mIDBConnection = {};
    const employeeService = new EmployeesService(mIDBConnection);
    (employeeService.findCount as jest.MockedFunction<any>).mockResolvedValueOnce([{ totalCount: 5 }]);
    (employeeService.findAll as jest.MockedFunction<any>).mockResolvedValueOnce([{ id: 1, name: 'john' }]);

    const mReq = {
      query: {
        pagesize: 10,
        page: 1,
      },
    };
    const mRes = {
      status: jest.fn().mockReturnThis(),
      json: jest.fn(),
    };
    const employeesController = new EmployeesController(mIDBConnection);
    await employeesController.findAllEmployees(mReq, mRes);
    expect(employeeService.findCount).toHaveBeenCalledTimes(1);
    expect(employeeService.findAll).toBeCalledWith(10, 1);
    expect(mRes.status).toBeCalledWith(200);
    expect(mRes.status().json).toBeCalledWith({ employees: [{ id: 1, name: 'john' }], maxEmployees: 5 });
  });
});

Unit test result with coverage report:

 PASS  src/stackoverflow/59235639/Employee.test.ts (11.243s)
  Employees
    ✓ should get count of employees (13ms)

-----------------------|----------|----------|----------|----------|-------------------|
File                   |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------------------|----------|----------|----------|----------|-------------------|
All files              |    88.89 |    66.67 |      100 |    86.67 |                   |
 employeeController.ts |    88.89 |    66.67 |      100 |    86.67 |             18,29 |
-----------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        12.958s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59235639

Upvotes: 3

Related Questions