【问题标题】:how to mock constructor in sub third party attribute using jest如何使用 jest 在子第三方属性中模拟构造函数
【发布时间】:2019-06-15 23:46:39
【问题描述】:

我们有一个简单的快递应用。我们使用 winston 作为第三方来正确处理日志记录。

const express = require('express');
const app = express();
const { createLogger, transports } = require('winston');

const port = process.env.PORT || 5000;
const logger = createLogger({ transports: [ new transports.Console()] });

const { catchAll } = require('./routes/catchall');

app.get('*', catchAll);

app.listen(port, () => logger.log({ level: 'info', message: `server listening on port ${port}!` }));

我们希望,在我们的单元测试中,检查 createLogger 是否已使用正确的参数调用。

我们一开始只尝试使用jest.mock('winston),但开玩笑地抱怨TypeError: transports.Console is not a constructor

然后我们尝试使用以下方法手动模拟包:

let { createLogger, transports } = require('winston');

const consoleClass = class Console{};
jest.doMock('winston', () => {
  return {
    createLogger: jest.fn(),
    transports: {
      Console: consoleClass
    }
  }
});

describe('logger', () => {
  it('should create proper logger', () => {
    expect(createLogger).lastCalledWith({ transports: [ new transports.Console()] })
  });
});

关于控制台的错误消失了。但是我们的测试失败了:

logger › should create proper logger

expect(jest.fn())[.not].lastCalledWith()

jest.fn() value must be a mock function or spy.
Received:
  function: [Function anonymous]

  39 | describe('logger', () => {
  40 |   it('should create proper logger', () => {
> 41 |     expect(createLogger).lastCalledWith({ transports: [ new transports.Console()] })
     |                          ^
  42 |   });
  43 | });
  44 |

  at Object.lastCalledWith (tests/app.spec.js:41:26)

我们尝试在doMock之后添加

createLogger.mockImplementation(() => {});

但它抱怨TypeError: createLogger.mockImplementation is not a function

如何同时替换 transports 属性和 createLogger?

我们需要能够模拟 createLogger 的实现,然后才能在另一个测试中断言 app.listen 已使用 logger.log 和适当的参数调用。

【问题讨论】:

    标签: javascript node.js jestjs winston


    【解决方案1】:

    试试下面的一个:

    const mockCreateLogger = jest.fn().mockImplementation(() => {
      log: jest.fn()
    });
    const mockConsole = jest.fn().mockImplementation(() => () => {});
    jest.mock('winston', () => ({
      createLogger: mockCreateLogger, // name must start with mock prefix
      transports: {
        Console: mockConsole // name must start with mock prefix
      }
    }));
    
    // here import the file you are testing after the mocks
    
    describe('logger', () => {
      it('should create proper logger', () => {
        expect(mockCreateLogger).toHaveBeenCalledWith({ transports: expect.anything() })
      });
    });
    

    附言为了在此处正确编写单元测试,您还应该模拟 express 以便在模拟的 app 上工作,而不是导入“真实”快递

    【讨论】:

    • 您的代码返回TypeError: Cannot read property 'Console' of undefined。 Transports 不是一种方法,它是一种属性,所以我猜mockTransports = jest.fn().mockImplementation 不能以这种方式完成。 Express 已经被嘲笑我简化了测试,只显示记录器的相关部分:)
    • 它再次抱怨 TypeError: Cannot read property 'Console' of undefined 7 |常量端口 = process.env.PORT || 5000; > 8 | const logger = createLogger({ transports: [ new transports.Console()] });
    • 您是否像在 sn-p 中一样添加了括号? new mockTransports()
    • 是的。抱怨是在我的应用程序文件中引用我的记录器,而不是测试文件:) 因为将 transprt 更改为函数并不能取代脚本在现实生活中期望属性的需要......const logger = createLogger({ transports: [ new transports.Console()] });
    • 哦,是的,对不起,我的错。我已经更改了 sn-p,现在尝试使用更新的,让我们看看问题是否已被绕过,然后我们将使用正确的实例更改 anything()
    猜你喜欢
    • 2018-11-17
    • 2019-12-14
    • 2018-02-19
    • 1970-01-01
    • 1970-01-01
    • 2020-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多