首页 > 解决方案 > 用 Jest 测试控制器和服务

问题描述

我对 Jest 相当陌生,并且一直在尝试(没有运气)弄清楚如何为我的控制器编写测试。我不确定如何编写测试,因为它调用了另一个函数。如果我至少能指出正确的方向,那就太好了。提前致谢。

控制器.ts

import * as Services from './services';

export async function GetCountriesList(req: Request, res: Response): Promise<void> {
    const response = await Services.GetCountriesList();
    res.status(response.code).json({
        status: response.status,
        message: response.message,
        count: response.count,
        data: response.data
     });
}

服务.ts

import db from '../../modules/db';
import { DBGenericDataResponse } from '../../types/models';

export async function GetCountriesList(): Promise<DBGenericDataResponse> {
    const lQuery = 'somquery';
    const responseMessage: DBGenericDataResponse = {
        code: 200,
        status: 'ok',
        message: '',
        count: 0,
        data: [],
        error: ''
    };
    try {
        const dbResult = await db.query<any>(lQuery);
        responseMessage.message = 'Countries returned';
        responseMessage.count = dbResult.rows.length;
        responseMessage.data = dbResult.rows;
    } catch (err) {
        responseMessage.code = 400;
        responseMessage.status = 'error';
        responseMessage.message = 'Error retrieving Countries List';
        responseMessage.error  = err;
    }
    return responseMessage;
}

标签: javascripttypescriptunit-testingtestingjestjs

解决方案


您应该使用jest.mock(moduleName, factory, options)方法来模拟./services模块。

例如

controller.ts

import * as Services from './services';
import type { Request, Response } from 'express';

export async function GetCountriesList(req: Request, res: Response): Promise<void> {
  const response = await Services.GetCountriesList();
  res.status(response.code).json({
    status: response.status,
    message: response.message,
    count: response.count,
    data: response.data,
  });
}

services.ts

export async function GetCountriesList() {
  return { code: 200, status: 200, message: 'real message', count: 1, data: 'real data' };
}

controller.test.ts

import { GetCountriesList } from './controller';
import * as Services from './services';
import { mocked } from 'ts-jest/utils';
import type { Request, Response } from 'express';

jest.mock('./services');

describe('GetCountriesList', () => {
  afterAll(() => {
    jest.resetAllMocks();
  });
  it('should pass', async () => {
    mocked(Services.GetCountriesList).mockResolvedValueOnce({
      code: 400,
      status: 400,
      message: 'fake message',
      count: 0,
      data: 'fake data',
    });
    const mReq = ({} as unknown) as Request;
    const mRes = ({ status: jest.fn().mockReturnThis(), json: jest.fn() } as unknown) as Response;
    await GetCountriesList(mReq, mRes);
    expect(mRes.status).toBeCalledWith(400);
    expect(mRes.json).toBeCalledWith({ status: 400, message: 'fake message', count: 0, data: 'fake data' });
  });
});

单元测试结果:

 PASS  examples/66449420/controller.test.ts
  GetCountriesList
    ✓ should pass (5 ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |   83.33 |      100 |      50 |   83.33 |                   
 controller.ts |     100 |      100 |     100 |     100 |                   
 services.ts   |      50 |      100 |       0 |      50 | 2                 
---------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.132 s, estimated 5 s

源代码:https ://github.com/mrdulin/jest-v26-codelab/tree/main/examples/66449420


推荐阅读