如何使用node-config在运行时覆盖配置值?

12

我想在测试时覆盖一些值,具体来说是将我的http服务的重试次数设置为1(立即失败,不进行重试)。我们的项目使用node-config。根据文档,我可以使用NODE_CONFIG环境变量进行覆盖:

node myapp.js --NODE_CONFIG='{"Customer":{"dbConfig":{"host":"customerdb.prod"}}}'

我更喜欢在我的测试中这样做,但并非所有测试都需要。根据代码,你可以通过设置ALLOW_CONFIG_MUTATIONS来允许配置变异。

process.env.ALLOW_CONFIG_MUTATIONS = "true";
const importFresh = require('import-fresh');
importFresh("config");

process.env.NODE_CONFIG = JSON.stringify({httpServices:{integration:{enrich: {retryInterval: 1, retries: 1}}}});
expect(process.env.NODE_CONFIG, 'NODE_CONFIG not set').to.exist();
expect(process.env.NODE_CONFIG, 'NODE_CONFIG not set').to.match(/retryInterval/);
expect(process.env.ALLOW_CONFIG_MUTATIONS, 'ALLOW_CONFIG_MUTATIONS not set').to.equal("true");

const testConfig = require("config");
console.dir(testConfig.get("httpServices.integration.enrich"));
expect(testConfig.get("httpServices.integration.enrich.retryInterval"), 'config value not set to 1').to.equal(1);

结果:

{ url: 'https://internal-**********',
  retryInterval: 5000,
  retries: 5 }
 `Error: config value not set to 1: Expected 5000 to equal specified value: 1`

我该如何让这个覆盖生效?(expect来自于Hapi.js代码库)
4个回答

8
我是 node-config 的维护者之一。你的bug在于第二次使用了 require,而应该再次使用 importFresh
你第一次使用 "importFresh()" 和 require() 没有任何区别,因为它是第一次使用 require()
在设置了一些变量后,你调用了 require(),它将返回已生成和缓存的 config 副本,忽略了设置的环境变量的影响。
你只需要在目前使用 require() 的地方使用一次 importFresh() 即可。这会返回一个“新鲜”的配置对象副本,正如你所期望的那样。

1
那个有效。我现在明白了,看过之后就知道了。不过你的回答可能需要更多的解释。 - jcollum

3

对我来说,只需更改config的属性即可解决问题。 例如:

const config = require( 'config' ); 

config.httpServices.integration.enrich.retryInterval = 1;

// Do your tests...

更新:确保在任何人调用第一个config.get()之前进行覆盖,因为一旦任何客户端通过get()使用值,config对象就会变为不可变。


1

我加入得比较晚,但其他答案不符合我项目中的测试标准,所以这是我想出来的方法:

简短回答

使用模拟对象(mocks)..

详细回答

node-config使用一个函数get来获取配置值。 通过模拟get函数,您可以轻松修改任何您认为合适的配置..

我个人最喜欢的库是sinon

这里是使用sinon进行模拟的实现示例

const config = require('config');
const sinon = require('sinon');
class MockConfig {
    constructor () {
       this.params = {};
       this.sandbox = sinon.sandbox.create();
    }
    withConfValue (confKey, confValue) {
        this.params.confValues[confKey] = confValue;
        return this;
    }
    reset () {
        this.params.confValues: {};
        return this;
    }
    restore() {
       this.sandbox.restore();
    }
    apply () {
        this.restore(); // avoid duplicate wrapping
        this.sandbox.stub(config, 'get').callsFake((configKey) => {
            if (this.params.confValues.hasOwnProperty(configKey)) {
                return this.params.confValues[configKey];
            }
            // not ideal.. however `wrappedMethod` approach did not work for me
            // https://dev59.com/LmEh5IYBdhLWcg3wMA0t#57017971
            return configKey
               .split('.')
               .reduce((result, item) => result[item], config)
            
        });
    }
}

const instance = new MockConfig();
MockConfig.instance = () => instance;

module.exports = MockConfig;

使用方法如下:

const mockConfig = require('./mock_config').instance();

...
beforeEach(function () {
    mockConfig.reset().apply();
})

afterEach(function () {
     mockConfig.reset().clear();
})


it('should do something') {
    mockConfig.withConfValue('some_topic.some_field.property', someValue);
    ... rest of the test ... 
}

假设

这种方法唯一的假设是您遵循node-config读取配置的方式(使用get函数),而不是直接访问字段来绕过它。


0
最好在您的配置文件夹中创建 development.json、production.json 和 test.json,node-config 将使用它来配置您的应用程序。您只需要设置 NODE_ENV 来使用特定的文件即可。 希望能有所帮助 :)

2
不是真的。我想要能够在特定的测试中更改我的配置,而不是所有测试都更改。正如我所提到的:“好吧,我更喜欢在我的测试中这样做,但并非所有测试都需要这样做。” - jcollum
@jcollum 好的,我明白了。你尝试过像这样导出配置吗:export NODE_CONFIG='{"Customer":{"dbConfig":{"host":"customerdb.prod"}}}' - Stan Sarr
你看了我的源代码吗?它似乎回答了你的问题。 - jcollum
我是指在你的 .bashrc / .bash_profile 中导出它,而不是通过添加 --NODE_CONFIG。 - Stan Sarr
但这意味着我要为所有测试设置它。或者我必须为每个配置单独调用NPM测试。这不是最优的方案。 - jcollum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接