typescript - 在 typescript 中 Jest mock express 和 Mongoose

标签 typescript express mongoose jestjs

我一直很难找到关于如何使用 jest.fn() 模拟 typescript 类和类方法的好资源(例如 express 的 RequestResponseNextFunction,以及 mongoose 模型上的 save() 方法。)

例如,假设我有以下模型和 Controller :

模型/Foo.ts:

import * as mongoose from "mongoose"

export type FooModel = mongoose.Document & {
  owner: mongoose.Schema.Types.ObjectId,
  bars: string[]
}

const fooSchema = new mongoose.Schema({
  owner: { type: mongoose.Schema.Types.ObjectId, ref: "User", index: true },
  bars: [String]
}

export const Foo = mongoose.model<FooModel>("Foo", fooSchema)

Controller /foo.ts:

import { Request, Response, NextFunction } from "express";
import { Foo, FooModel } from "../models/Foo";

export let createFoo = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const foo = new Foo({
      owner: req.user._id,
      bars: req.body.bars
    });
    await foo.save();
    res.status(200).json(foo);
  } catch (err) {
    next(err)
  }
}

我想添加一些单元测试:

import { Request, Response, NextFunction } from "express";
import { Foo } from "../../src/models/Foo";
import * as fooController from "../../src/controllers/foo";
import {} from "jest";

describe("createFoo", async () => {
  let req: Request;
  let res: Response;
  let next: NextFunction;
  const bars = ["baz", "qux", "quux"];

  beforeEach(() => {
    // Mock req, res and next
    // Set req.body.bars equal to bars
    // Stub out Foo.save() so that we don't actually save to the db
  });

  it("should add bars to the foo", async () => {
    await fooController.createFoo(req, res, next);
    responseData = JSON.parse(res.json)
    expect(responseData.bars).toEqual(bars);
  });

  it("should save the foo", async () => {
    await fooController.createFoo(req, res, next);
    expect(Foo.prototype.save).toHaveBeenCalled();
  }

  it("should call next on error", async () => {
    const err = new Error();
    // Set up Foo.save() to throw err
    await fooController.createFoo(req, res, next);
    expect(next).toHaveBeenCalledWith(err);
  }
});

我遇到的主要问题是注释掉的部分:我还没有弄清楚如何实际模拟 reqresnext ,或如何停止 Foo.save() 或使其抛出错误。我怎样才能做到这一点?

最佳答案

要模拟 Request 和 Response 对象,您只需将它们与您正在使用的值一起传递即可。在您的情况下,这看起来像这样:

import { Request, Response, NextFunction } from 'express';
import { Foo } from '../../src/models/Foo';
import * as fooController from '../../src/controllers/foo';
import { doesNotReject } from 'assert';

describe('createFoo', async () => {
  it('should add bars to the foo', async () => {
    /**
     * Spy on the model save function and return a completed promise.
     * We are not testing the model here, only the controller so this is ok.
     */
    jest.spyOn(Foo.prototype, 'save').mockImplementationOnce(() => Promise.resolve());

    /**
     * Create a mock request and set type to any to tell typescript to ignore type checking
     */
    const mockRequest: any = {
      user: {
        _id: 1234,
      },
      body: {
        bars: ['baz', 'qux', 'quux'],
      },
    };

    /**
     * Create a mock repsonse with only the methods that are called in the controller and
     * record their output with jest.fn()
     */
    const mockResponse: any = {
      status: jest.fn(),
      json: jest.fn(),
    };

    /**
     * Create a mock next function. It is okay to set its type to Express's NextFunction because
     * we are mocking the entire function.
     */
    const mockNext: NextFunction = jest.fn();

    await fooController.createFoo(mockRequest, mockResponse, mockNext);

    expect(mockResponse.json).toHaveBeenCalledTimes(1);
    expect(mockResponse.json).toHaveBeenCalledWith('bars');

    expect(mockResponse.status).toHaveBeenCalledTimes(1);
    expect(mockResponse.status).toHaveBeenCalledWith(200);
  });
});

希望对您有所帮助!

关于typescript - 在 typescript 中 Jest mock express 和 Mongoose ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48218489/

相关文章:

node.js - MongoDB:尝试使用聚合 $unwind 来获取嵌入式文档

AngularJS 打印部分页面和数据绑定(bind)

javascript - httpParams 删除未定义的参数

session - 对 node.js、connect-mongo、 session 覆盖的并行请求

javascript - 尝试将 promise 的 Angular 动态数组发送到表达服务器

javascript - for 循环内的 async.waterfall 逃脱了 for 循环

mongodb - Mongodb聚合$ group,限制数组的长度

typescript - 如何在 TypeScript 中使用 router.navigate 打开新标签页

javascript - 在每个文档上单击我想在 debounceTime() 函数中传递不同的值

mysql - 如何使用 nodejs 和 ajax 使 typeahead 显示来自 mysql 的信息