¿Cómo stub process.env en node.js?

81

Quiero tropezar process.env.FOOcon bar.

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

Estoy confundido. Leí el documento, pero todavía no lo entiendo. sinonjs docs

sinonjs es un ejemplo, no sinonjs está bien.

Matt - sanemat
fuente
¿Puede explicar por qué querría stub de las variables de entorno? ¿Está haciendo esto en un sistema operativo similar a Unix o Windows?
slebetman
1
@slebetman es común confiar en variables de entorno para la configuración, como una clave de API para un servicio en el que confía. Consulte 12factor.net .
Andrew Homeyer
1
@AndrewHomeyer: Sí, pero no muñón de ellos - los configura correctamente para la prueba
slebetman

Respuestas:

74

Según tengo entendido process.env, simplemente puede tratarlo como cualquier otra variable al establecer sus propiedades. Sin embargo, process.envtenga en cuenta que cada valor debe ser una cadena. Entonces, si necesita un valor particular en su prueba:

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

Para evitar filtrar el estado en otras pruebas, asegúrese de restablecer la variable a su valor original o elimínela por completo:

   afterEach(() => {
       delete process.env.NODE_ENV;
   });
Joshua Dutton
fuente
8
Esto funciona para mi. Una cosa a tener en cuenta es que si está probando un módulo que lee NODE_ENV cuando el módulo se carga por primera vez, probablemente querrá configurar NODE_ENV antes de cargar el módulo (es decir, NODE_ENV se puede configurar en un bloque beforeEach). Esto puede parecer obvio , pero me ha hecho tropezar antes.
Terrence
Si tiene problemas, ¿puede publicar un fragmento de código para que alguien lo vea? Escribí mi respuesta con la sintaxis del corredor de prueba de Mocha en mente, pero también debería funcionar con cualquier otro corredor (por ejemplo, laboratorio).
Joshua Dutton
2
Esto funciona, pero encontré una peculiaridad al usar jest. En mi código de producción asigné de env a una constante (por ejemplo const X = process.env.X). La const se declaró en el ámbito del módulo (ES), no en el ámbito de la función. Mis pruebas siempre pasaban con jest --watchlas ejecuciones de prueba reintentadas, pero siempre fallaban en la primera ejecución. Hay un problema de pedido que no entiendo completamente aquí. Solo asegúrese de estar siempre leyendo directamente desde process.envsu código de producción (es decir, en una función), y no almacenando en caché a nivel de módulo.
Jesse Buchanan
1
esto funciona bien si está evaluando process.env en una función, pero no si es una constante. por ejemplo, const myValue = process.env.value ? process.env.value : 'default'no funcionaría si configurara process.env.value dentro de una prueba. Sin embargo, const myValue = () => (process.env.value ? process.env.value : 'default') funciona como se esperaba.
Rafael Marques
En esta misma línea, tuve: const SWITCH_ON = (process.env.SWITCH_ON.toLowerCase() === 'true');que no funcionó, así que lo cambié a dos líneas: var switchOn = process.env.SWITCH_ON; const SWITCH_ON = (switchOn === undefined ? false : switchOn.toLowerCase() === 'true');La inicial seguía dándome undefinederrores donde estaba haciendo el.toLowerCase()
Entusiasta de Scala
25

Pude conseguir process.envque me aprueben correctamente en mis pruebas unitarias clonándolo y en un método de desmontaje restaurándolo.

Ejemplo usando Mocha

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

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

...

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

Tenga en cuenta que esto solo funcionará si process.env solo se lee en la función que está probando. Por ejemplo, si el código que está probando lee la variable y la usa en un cierre, no funcionará. Probablemente invalide el requisito almacenado en caché para probarlo correctamente.

Por ejemplo, lo siguiente no tendrá el env codificado:

const nodeEnv = process.env.NODE_ENV;

const fnToTest = () => {
   nodeEnv ...
}
por favor
fuente
3
Este proceso funcionó principalmente. Tuve que modificar el método "después". after(() => { process.env = Object.assign({}, env); }); De lo contrario, las pruebas manipularían la copia compartida. Es necesario configurar después de cada prueba una versión nueva.
Kyle
1
@Kyle ... no, ¿no? asumiendo que configuró env una vez en la parte superior de su archivo, se restaurará a lo que era al comienzo de su suite de pruebas ..
Prisoner
4

En uno spec-helper.coffeeo algo similar en el que configuras tu caja de arena de Sinon, haz un seguimiento del original process.envy restáuralo después de cada prueba, para que no tengas fugas entre las pruebas y no tengas que acordarte de reiniciar cada vez.

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

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

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

En su prueba, utilícelo process.envnormalmente.

it 'does something based on an env var', ->
    process.env.FOO = 'bar'
Andrew Homeyer
fuente
underscoreLa clonefunción de funciona en lugar de cloneDeep: útil si ya está usando en underscorelugar de lodash.
Rob
4

Con sinon puede stub cualquier variable como esta.

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

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

 myObj.example; // 'newValue'

Este ejemplo es documentación de formulario sinon. https://sinonjs.org/releases/v6.1.5/stubs/


Con ese conocimiento, puede apuntar cualquier variable de entorno. En su caso, se vería así:

 let stub = sinon.stub(process.env, 'FOO').value('bar');
Getriax
fuente
5
Recibí un error "No se puede apuntar una propiedad propia no existente FOO". También uso wallaby.js para ejecutar mis pruebas.
Will Lovett
1
Gracias por publicar la respuesta a la pregunta "¿Cómo se ve el stubbing de una var env?" en lugar de simplemente decir que no es necesario porque podemos manipularlos manualmente :)
Will
Tuve el mismo error que @WillLovett y lo resolví agregando la llamada require cerca de la parte superior de mi script de prueba de unidad: require('dotenv').config();me di cuenta de que normalmente se llama a esto cuando se ejecuta mi aplicación, pero si estoy ejecutando mis pruebas de unidad directamente, esto Requerir declaración faltaría.
Von Pittman
4

Cómo simular rápidamente process.env durante las pruebas unitarias.

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()
})

Pero, ¿qué pasa con las propiedades que podrían no existir en process.env antes de la prueba? Puede usar el siguiente paquete y luego podrá probar las variables env no existentes.

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

Hossein Rabizadeh
fuente
Esto no funcionará cuando process.env.USERaún no tenga un valor.
Sohail Si