在 mocha 测试之间重新导入模块

Posted

技术标签:

【中文标题】在 mocha 测试之间重新导入模块【英文标题】:Re-importing modules between mocha tests 【发布时间】:2018-10-04 18:37:19 【问题描述】:

在我的 node/typescript express 应用程序中,我将配置设置存储在settings.json 文件中,该文件由config.ts 作为对象加载和导出。每个使用配置设置的模块都像这样导入模块:

import Config from './config';

config.ts 看起来像这样(在此示例中进行了简化):

class Config 
  public static get(): any 
    const settings = require('settings.json');
    return settings;
  


export default Config.get();

当应用程序运行时,这一切正常。但是我的摩卡咖啡测试有问题。在某些测试中,我想在触发应用功能之前更改配置设置(例如Config.someSetting = 'someValue'),然后在运行下一个测试之前将配置设置重置为默认值。

我知道我可以手动将每个更改的配置值重置为默认值,但理想情况下我想“重新导入”config.ts 模块,它将所有配置设置重置为默认值。我的问题是最好的方法是什么?

我尝试使用decache 并将以下内容添加到afterEach

decache('./config');

尽管我可以看到 config.ts 不再在 require 缓存中,但 Config 对象仍然存在,它的所有后续测试的当前值(config.ts 没有被“重新导入”)。

我做错了什么?

【问题讨论】:

不确定将测试迁移到 Jest 是否可行,但 Jest 测试框架通过 jest.resetModules 内置了对此的支持:jestjs.io/docs/en/jest-object.html#jestresetmodules 【参考方案1】:

我发现的最佳方法是使用proxyquire

const proxyquire = require('proxyquire');

let moduleUnderTest;

describe('Given a Service Provider', () => 
    beforeEach(() => 
        proxyquire.noPreserveCache(); // Tells proxyquire to not fetch the module from cache

        // Ensures that each test has a freshly loaded instance of moduleUnderTest
        moduleUnderTest = proxyquire(
            '../../../../src/data/firebase/admin/service-provider',
             // Stub if you need to, or keep the object empty
        );
    );

    // Use moduleUnderTest as you like
);

【讨论】:

【参考方案2】:

如果在decache('settings.json') 之后重新评估require('settings.json')(即调用Config.get()),像decache 这样的缓存处理包应该可以工作。

由于修改的是settings.json模块对象,所以应该恢复。 decache 应该直接影响应该被取消缓存的包,即settings.json。如果Config.get() 没有被多次调用,./config' 和每个导入它的模块也应该被取消缓存。这使得在这种情况下使用decache 是不合理的。

这里的问题是配置模块对测试不友好。仅静态类是反模式。如果Config 没有像代码显示的那样导出,这也是反模式,因为它提供了一个抽象,不能在模块导出时多次使用。

为了改善这种情况,应该重构配置模块,使其能够在导入配置对象后重新评估使用配置对象的模块中的require('settings.json')

export default function getConfig() 
  return require('settings.json');

getConfig() 应始终按原样使用,不应在使用它的模块顶部分配const config = getConfig(),这将使其无法缓存。

目前恢复原始配置的一种方法是在保留对现有对象的引用的同时对其进行修改,例如:

afterEach(() => 
  decache('./settings.json');
  Object.assign(Config, require('./settings.json'));
);

可以看出。 Config.get 抽象没有任何帮助。

transpiled ES 模块中的另一种方法是直接修补模块对象。由于根据规范,模块对象应该是导出的只读反射。预计模块会被转译器(包括 TypeScript)相应地处理。这取决于应用程序的构建方式,可能无法在任何环境中按预期工作。

import Config from './config';
console.log(Config.foo);

应该转译成类似的东西

Object.defineProperty(exports, "__esModule",  value: true );    
console.log(config_1.default.foo;);

这可能允许动态破坏 ES 模块导出(CommonJS 模块默认导出不可能)并影响那些使用 Config 并重新评估的模块部分(例如,内部函数但不在***模块范围内):

afterEach(() => 
  decache('./settings.json');
  const configModule = require('./config'));
  configModule.default = require('./settings.json');
);

【讨论】:

以上是关于在 mocha 测试之间重新导入模块的主要内容,如果未能解决你的问题,请参考以下文章

运行 mocha 测试时 Babel 意外令牌导入

重新导入被测模块以丢失上下文

模拟模块哪个不存在?

修改 Python 模块的常见做法

模块和包

mocha 单元测试中的 Highchart 导入错误