用笑话模拟 fs 函数

Posted

技术标签:

【中文标题】用笑话模拟 fs 函数【英文标题】:Mock fs function with jest 【发布时间】:2018-10-08 12:28:32 【问题描述】:

首先,我是es6jest 的新手。

我有一个 Logger 类用于实例化 winston,我想对其进行测试。

这是我的代码:

const winston = require('winston');
const fs = require('fs');
const path = require('path');
const config = require('../config.json');

class Logger 
  constructor() 
    Logger.createLogDir(Logger.logDir);
    this.logger = winston.createLogger(
      level: 'info',
      format: winston.format.json(),
      transports: [
        new (winston.transports.Console)(
          format: winston.format.combine(
            winston.format.colorize( all: true ),
            winston.format.simple(),
          ),
        ),
        new (winston.transports.File)(
          filename: path.join(Logger.logDir, '/error.log'),
          level: 'error',
        ),
        new (winston.transports.File)(
          filename: path.join(Logger.logDir, '/info.log'),
          level: 'info',
        ),
        new (winston.transports.File)(
          filename: path.join(Logger.logDir, '/combined.log'),
        ),
      ],
    );
  

  static get logDir() 
    return (config.logDir == null) ? 'log' : config.logDir;
  

  static createLogDir(logDir) 
    if (!fs.existsSync(logDir)) 
      // Create the directory if it does not exist
      fs.mkdirSync(logDir);
    
  


exports.logger = new Logger().logger;
export default new Logger();

我想测试我的函数createLogDir()。 我的脑袋,我认为测试 fs.existsSync 的状态是个好主意。 如果fs.existsSync 返回false,则必须调用fs.mkdirSync。 所以我尝试写一些jest 测试:

describe('logDir configuration', () => 
  test('default path must be used', () => 
    const logger = require('./logger');
    jest.mock('fs');
    fs.existsSync = jest.fn();
    fs.existsSync.mockReturnValue(false);
    const mkdirSync = jest.spyOn(logger, 'fs.mkdirSync');
    expect(mkdirSync).toHaveBeenCalled();
  );
);

但是,我有一个错误:

  ● logDir configuration › default path must be used

    Cannot spy the fs.mkdirSync property because it is not a function; undefined given instead

      18 |     fs.existsSync = jest.fn();
      19 |     fs.existsSync.mockReturnValue(true);
    > 20 |     const mkdirSync = jest.spyOn(logger, 'fs.mkdirSync');
      21 |     expect(mkdirSync).toHaveBeenCalled();
      22 |   );
      23 | );

      at ModuleMockerClass.spyOn (node_modules/jest-mock/build/index.js:590:15)
      at Object.test (src/logger.test.js:20:28)

你能帮我调试和测试我的功能吗?

问候。

【问题讨论】:

【参考方案1】:

那里的错误是因为它正在您的logger 对象上寻找一个名为fs.mkdirSync 的方法,该对象不存在。如果您在测试中可以访问fs 模块,那么您将像这样监视mkdirSync 方法:

jest.spyOn(fs, 'mkdirSync');

但是,我认为您需要采取不同的方法。

您的createLogDir 函数是一个静态方法——这意味着它只能在类上调用,而不能在该类的实例上调用(new Logger() 是类Logger 的一个实例)。因此,为了测试该功能,您需要导出类而不是它的实例,即:

module.exports = Logger;

那么您可以进行以下测试:

const Logger = require('./logger');
const fs = require('fs');

jest.mock('fs') // this auto mocks all methods on fs - so you can treat fs.existsSync and fs.mkdirSync like you would jest.fn()

it('should create a new log directory if one doesn\'t already exist', () => 
    // set up existsSync to meet the `if` condition
    fs.existsSync.mockReturnValue(false);

    // call the function that you want to test
    Logger.createLogDir('test-path');

    // make your assertion
    expect(fs.mkdirSync).toHaveBeenCalled();
);

it('should NOT create a new log directory if one already exists', () => 
    // set up existsSync to FAIL the `if` condition
    fs.existsSync.mockReturnValue(true);

    Logger.createLogDir('test-path');

    expect(fs.mkdirSync).not.toHaveBeenCalled();
);

注意:看起来您正在混合使用 CommonJS 和 es6 模块语法(export default 是 es6) - 我会尽量坚持其中一个

【讨论】:

我认为这种模式不允许恢复真正的 FS 方法。这会以可能影响其他测试的方式永久改变测试环境。您需要使用jest.spyOn(fs, 'mkdirSync'),以便稍后使用fs.mkdirSync.mockRestore() @Billy winston 将自动创建新文件夹我们是否需要为该文件夹编写额外的代码 @Billy 我也在实现上面的那个你可以检查一下这个***.com/questions/54250372/…,对我有帮助 @Tom 你是对的,已更改为自动模拟,因此不会影响其他测试

以上是关于用笑话模拟 fs 函数的主要内容,如果未能解决你的问题,请参考以下文章

笑话:当同一个模块也有命名导出时,如何模拟默认导出组件?

使用笑话模拟时的打字稿错误

笑话测试库 - 模拟拒绝承诺不能按预期在 useAsync() 中工作

为归档函数 fs 创建单元测试

使用 Jest/Typescript 测试 fs 库函数

模拟特定的函数重载签名