在 mocha 测试之间重新导入模块
Re-importing modules between mocha tests
在我的 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'
),然后在 运行 下一次测试之前将配置设置重置为默认值。
我知道我可以手动将每个更改的配置值重置为默认值,但理想情况下我想 "re-import" config.ts
模块将所有配置设置重置为默认值。我的问题是最好的方法是什么?
我尝试使用 decache 并将以下内容添加到 afterEach
:
decache('./config');
即使我可以看到 config.ts
不再在 require 缓存中,Config 对象仍然存在,它的所有后续测试的当前值(config.ts
不是 "re-imported").
我做错了什么?
如果在 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');
});
我发现的最佳方法是使用 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
});
在我的 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'
),然后在 运行 下一次测试之前将配置设置重置为默认值。
我知道我可以手动将每个更改的配置值重置为默认值,但理想情况下我想 "re-import" config.ts
模块将所有配置设置重置为默认值。我的问题是最好的方法是什么?
我尝试使用 decache 并将以下内容添加到 afterEach
:
decache('./config');
即使我可以看到 config.ts
不再在 require 缓存中,Config 对象仍然存在,它的所有后续测试的当前值(config.ts
不是 "re-imported").
我做错了什么?
如果在 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');
});
我发现的最佳方法是使用 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
});