Jest 模拟每个单独测试的第三方默认导出

Jest mock third-party default export per individual test

我正在尝试使用 jestsupertest 测试 express 控制器。我有一个使用 multer 处理文件的中间件。我可以在全球范围内模拟 multer 但我无法在每个 it/test 个案例中模拟它。

// saveImage.controller.ts
jest.mock('multer', () => {
  const m = () => ({
    single: () => (req: Request, res: Response, next: NextFunction) => {
      req.body = { type: 'screenshots' };
      req.file = mockFile;
      return next();
    },
  });
  m.memoryStorage = () => jest.fn();
  return m;
});

describe('POST /api/v1/garment/image/upload', () => {
  it('should return 201', async () => {
    await request(app)
      .post('/api/v1/garment/image/upload')
      .send({ file: 'myImage' }) // this is overwritten by the mock 
      .expect(201); // passes
  });

  it('should return 400', async () => {
    await request(app)
      .post('/api/v1/garment/image/upload')
      .send() // this is overwritten by the mock
      .expect(400); // fails
  });
});

第一个测试通过了,但我无法更改模拟以使第二个测试通过。 如何更改我的第二个 it 案例中的模拟?

我会用jest.doMock(moduleName, factory, options)来模拟multer。注意multer.single()方法会在模块导入的时候执行,所以我们在模块导入之前mock它。

为了在导入模块时清除缓存(可以缓存模块作用域变量,函数的函数等),需要在运行之前使用jest.resetModules() each测试用例。

app.ts:

import express from 'express';
import multer from 'multer';
import path from 'path';

const upload = multer({ dest: path.join(__dirname, 'uploads/') });
const app = express();

app.post('/api/v1/garment/image/upload', upload.single('file'), (req, res) => {
  console.log(req.file, req.body);
  if (req.file) {
    res.sendStatus(201);
  } else {
    res.sendStatus(400);
  }
});

export { app };

app.test.ts:

import request from 'supertest';
import multer from 'multer';
import { NextFunction, Request, Response } from 'express';

describe('69997349', () => {
  let app: typeof import('./app').app;
  beforeEach(async () => {
    jest.resetModules();
  });
  it('should return 201', async () => {
    const mockFile = { fieldname: 'myImage' } as Express.Multer.File;
    jest.doMock('multer', () => {
      const m = () => ({
        single: () => (req: Request, res: Response, next: NextFunction) => {
          req.body = { type: 'screenshots' };
          req.file = mockFile;
          return next();
        },
      });
      return m;
    });
    app = (await import('./app')).app;
    await request(app).post('/api/v1/garment/image/upload').send({ file: 'myImage' }).expect(201);
  });

  it('should return 400', async () => {
    jest.doMock('multer', () => {
      const m = () => ({
        single: () => (req: Request, res: Response, next: NextFunction) => {
          return next();
        },
      });
      return m;
    });
    app = (await import('./app')).app;
    await request(app).post('/api/v1/garment/image/upload').expect(400);
  });
});

测试结果:

 PASS  examples/69997349/app.test.ts (8.245 s)
  69997349
    ✓ should return 201 (297 ms)
    ✓ should return 400 (23 ms)

  console.log
    { fieldname: 'myImage' } { type: 'screenshots' }

      at examples/69997349/app.ts:10:11

  console.log
    undefined undefined

      at examples/69997349/app.ts:10:11

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        9.428 s, estimated 10 s