在玩笑中模拟猫鼬聚合函数

时间:2021-03-02 07:11:43

标签: node.js typescript unit-testing mongoose jestjs

我有一个中间件,如下所示,我正在尝试使用 jest 对其进行单元测试,

中间件:

import Ph from './Ph.model';
fAP = (req, res, next) => {
        const pN = req.body.pN;
        
        if (pN != null) {

            Ph.aggregate([
                { $match: { 'polNum': { $eq: pN } } },
                {
                    $match: {
                        $expr:
                        {
                            $and: [
                                { $eq: ['$pCS', 'A'] }
                            ]
                        }
                    }
                }
            ]).exec((err, data) => {

                if (data.length > 0) {
                    req.p = data[0];
                    req.body.pN = data[0].pN;
                    return next();
                } else {
                    return res.json({ message: 'Not found'});
                }

            });
        } else {
            return res.json({ message: 'Cannot be null.'});
        }
    }

测试代码:

import { Request, Response, NextFunction } from 'express';

import PhCtrl from 'Ph.controller';
const PhController = new PhCtrl();

import Ph from './Ph.model';

describe('PhController', () => {
  let mockRequest: any;
  let mockResponse: any;
  let mockNext: NextFunction = jest.fn();

  beforeEach(() => {
    mockRequest = {
      body: {
        polNum: ''
      }
    };
    mockResponse = {
      json: jest.fn()
    };
  });

  // fAP middleware
  describe('fAP middleware', () => {
    // pN is not null and matches
    describe('when pn is not null', () => {
      // found
      test('should call next when matches', async () => {
        const mockRequest: any = {
          body: {
            pN: '3028'
          }
        };
        const mockResponse: any = {
          json: jest.fn()
        };
        const mockNext: NextFunction = jest.fn();

        const responseData = require('../../../mock-data/p.json');
        
        Ph.aggregate = jest.fn().mockImplementationOnce(() => ({
          exec: jest.fn().mockResolvedValueOnce(responseData)
        }));

        await PhController.fAP(
          mockRequest,
          mockResponse,
          mockNext
        );
        expect(mockNext).toHaveBeenCalled();
      });
    });
  });
});

错误: 但是当我尝试模拟聚合函数时,我收到以下错误。 在 findOne 的情况下,类似的过程有效,但在 mongoose 的聚合方法中失败。任何帮助将不胜感激。

<块引用>

● PhController › fAP 中间件 › 当 pn 不为空 › 匹配时应调用 next

expect(jest.fn()).toHaveBeenCalled()

Expected number of calls: >= 1
Received number of calls:    0

  54 |           mockNext
  55 |         );
> 56 |         expect(mockNext).toHaveBeenCalled();
     |                          ^
  57 |       });
  58 | 
  59 |       // active ph is not found

  at tests/server/api/ph/ph.controller.test.ts:56:26

1 个答案:

答案 0 :(得分:1)

您可以使用 jest.mock(moduleName, factory, options) 方法模拟 './Ph.model 模块。

使用 mockFn.mockReturnThis() 来模拟方法链调用。

例如

middleware.ts

import Ph from './Ph.model';

export const fAP = (req, res, next) => {
  const pN = req.body.pN;

  if (pN != null) {
    Ph.aggregate([
      { $match: { polNum: { $eq: pN } } },
      {
        $match: {
          $expr: {
            $and: [{ $eq: ['$pCS', 'A'] }],
          },
        },
      },
    ]).exec((err, data) => {
      if (data.length > 0) {
        req.p = data[0];
        req.body.pN = data[0].pN;
        return next();
      } else {
        return res.json({ message: 'Not found' });
      }
    });
  } else {
    return res.json({ message: 'Cannot be null.' });
  }
};

Ph.model.ts

import mongoose from 'mongoose';

const schema = new mongoose.Schema();

const Ph = mongoose.model('Ph', schema);

export default Ph;

middleware.test.ts

import { NextFunction } from 'express';
import { fAP } from './middleware';
import { mocked } from 'ts-jest/utils';
import Ph from './Ph.model';

jest.mock('./Ph.model', () => {
  return {
    aggregate: jest.fn().mockReturnThis(),
    exec: jest.fn(),
  };
});

describe('66434808', () => {
  afterAll(() => {
    jest.resetAllMocks();
  });
  it('should call next when matches', () => {
    const responseData = [{ pN: '2222' }];
    mocked(Ph.aggregate().exec).mockImplementationOnce((callback) => {
      callback!(null, responseData);
    });
    const mockRequest: any = { body: { pN: '3028' } };
    const mockResponse: any = { json: jest.fn() };
    const mockNext: NextFunction = jest.fn();
    fAP(mockRequest, mockResponse, mockNext);
    expect(Ph.aggregate).toBeCalledWith([
      { $match: { polNum: { $eq: '3028' } } },
      {
        $match: {
          $expr: {
            $and: [{ $eq: ['$pCS', 'A'] }],
          },
        },
      },
    ]);
    expect(Ph.aggregate().exec).toBeCalledWith(expect.any(Function));
    expect(mockNext).toBeCalledTimes(1);
  });
});

单元测试结果:

 PASS  examples/66434808/middleware.test.ts
  66434808
    ✓ should call next when matches (4 ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |   83.33 |       50 |     100 |   81.82 |                   
 middleware.ts |   83.33 |       50 |     100 |   81.82 | 22-26             
---------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.362 s

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

相关问题