如何在node.js中存根process.env?


81

我想process.env.FOObar

var sinon = require('sinon');
var stub = sinon.stub(process.env, 'FOO', 'bar');

我糊涂了。我阅读了文档,但仍然不了解。sinonjs文档

sinonjs是一个示例,不是sinonjs可以。


您能解释一下为什么要对环境变量进行存根吗?您是在类似Unix的操作系统还是Windows上执行此操作?
slebetman 2014年

1
@slebetman通常依靠环境变量进行配置,例如您所依赖的服务的API密钥。参见12factor.net
Andrew Homeyer

1
@AndrewHomeyer:是的,但是您不存根-您为测试正确设置了它们
slebetman

Answers:


74

据我了解process.env,您可以在设置其属性时像对待其他任何变量一样简单地对待它。但是请记住,其中的每个值都process.env必须是字符串。因此,如果您在测试中需要特定的值:

   it('does something interesting', () => {
      process.env.NODE_ENV = 'test';
      // ...
   });

为避免将状态泄漏到其他测试中,请确保将变量重置为其原始值或将其完全删除:

   afterEach(() => {
       delete process.env.NODE_ENV;
   });

8
这个对我有用。要记住的一件事是,如果要测试的模块在首次加载时读取NODE_ENV,则可能需要加载模块之前设置NODE_ENV (即,可以在beforeEach块中设置NODE_ENV。)这似乎很明显。 ,但之前让我绊倒了。
Terrence

如果遇到问题,可以张贴代码片段供他人查看吗?我在编写答案时就牢记了Mocha测试运行程序的语法,但它也应与其他任何运行程序一起使用(例如,实验室)。
Joshua Dutton

2
这可行,但是使用时我发现了一个怪癖jest。在生产代码中,我从env分配给const(例如const X = process.env.X)。const是在(ES)模块作用域而不是函数作用域声明的。我的测试总是jest --watch在重试测试中通过,但是在第一次运行中总是失败。这里有一个我不完全了解的订购问题。只要确保您始终从process.env生产代码(即函数)中直接读取,并且不将其缓存在模块级别即可。
Jesse Buchanan

1
如果您在函数中评估process.env,则此方法效果很好,但如果它是一个常量,则效果不佳。例如,const myValue = process.env.value ? process.env.value : 'default'如果您在测试中设置process.env.value ,我将无法工作。但是,const myValue = () => (process.env.value ? process.env.value : 'default')可以正常工作!
拉斐尔·马克斯

同样,我有:没用,const SWITCH_ON = (process.env.SWITCH_ON.toLowerCase() === 'true');所以我将其更改为两行:var switchOn = process.env.SWITCH_ON; const SWITCH_ON = (switchOn === undefined ? false : switchOn.toLowerCase() === 'true');最初的代码undefined在执行过程中一直给我带来错误.toLowerCase()
Scala发烧友

25

process.env通过克隆和恢复它的拆卸方法,我能够在我的单元测试中被正确地打败。

使用摩卡的例子

const env = Object.assign({}, process.env);

after(() => {
    process.env = env;
});

...

it('my test', ()=> {
    process.env.NODE_ENV = 'blah'
})

请记住,仅在正在测试的函数中读取process.env时,此方法才有效。例如,如果您要测试的代码读取变量并在闭包中使用它,则它将无法工作。您可能会使缓存的需求无效以正确测试。

例如,以下代码不会包含env:

const nodeEnv = process.env.NODE_ENV;

const fnToTest = () => {
   nodeEnv ...
}

3
这个过程大部分有效。我不得不调整“之后”方法。after(() => { process.env = Object.assign({}, env); }); 否则,测试将操纵共享副本。每次测试后需要设置一个新版本。
凯尔(Kyle)

1
@Kyle ..不,不是吗?假设你设置包膜一旦你的文件的顶部,它会恢复到什么它是在您的测试套件的开始..
囚徒

4

spec-helper.coffee或类似的地方,您可以设置sinon沙箱,跟踪原始文件process.env,并在每次测试后将其还原,这样您就不会在两次测试之间泄漏,也不必每次都记得重置。

_ = require 'lodash'
sinon = require 'sinon'

beforeEach ->
    @originalProcessEnv = _.cloneDeep process.env

afterEach ->
    process.env = _.cloneDeep @originalProcessEnv

在测试中,请process.env照常使用。

it 'does something based on an env var', ->
    process.env.FOO = 'bar'

underscoreclone功能代替了cloneDeep-如果您已经在使用underscore而不是lodash
罗布

4

使用sinon,您可以像这样存根任何变量。

 const myObj = {
    example: 'oldValue', 
 };

 sinon.stub(myObj, 'example').value('newValue');

 myObj.example; // 'newValue'

此示例是表单sinon文档。https://sinonjs.org/releases/v6.1.5/stubs/


有了这些知识,您就可以对任何环境变量进行存根。在您的情况下,它看起来像这样:

 let stub = sinon.stub(process.env, 'FOO').value('bar');

5
我收到错误消息“无法存根不存在的自有财产FOO”。还使用wallaby.js来运行我的测试。
Will Lovett

1
感谢您发布问题“对环境变量进行存根是什么样的问题”的答案?而不是只是说,我们并不需要,因为我们可以手动操纵他们:)
威尔

我遇到了与@WillLovett相同的错误,并通过在单元测试脚本顶部附近添加了require调用来解决了这个问题: require('dotenv').config();我意识到通常在我的应用程序运行时会调用该调用,但是如果我直接运行单元测试,这require语句将丢失。
冯·皮特曼

4

如何在单元测试期间快速模拟process.env。

https://glebbahmutov.com/blog/mocking-process-env/

const sinon = require('sinon')
let sandbox = sinon.createSandbox()

beforeEach(() => {
  sandbox.stub(process.env, 'USER').value('test-user')
})

it('has expected user', () => {
  assert(process.env.USER === 'test-user', 'wrong user')
})

afterEach(() => {
  sandbox.restore()
})

但是,在测试之前,process.env中可能不存在的属性又如何呢?您可以使用以下程序包,然后可以测试不存在的env变量。

https://github.com/bahmutov/mocked-env


process.env.USER没有值时,它将不起作用。
Sohail Si
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.