Jak odgiąć process.env w node.js?

81

Chcę się process.env.FOOz tym pogodzić bar.

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

Jestem zmieszany. Przeczytałem dokument, ale nadal nie rozumiem. sinonjs docs

sinonjs jest jednym z przykładów, nie sinonjs jest w porządku.

Matt - sanemat
źródło
Czy możesz wyjaśnić, dlaczego chcesz zablokować zmienne środowiskowe? Czy robisz to w systemie operacyjnym typu unix lub Windows?
slebetman
1
@slebetman często polega na zmiennych środowiskowych podczas konfiguracji, takich jak klucz API dla usługi, na której polegasz. Zobacz 12factor.net .
Andrew Homeyer
1
@AndrewHomeyer: Tak, ale nie skrótową je - je prawidłowo ustawione dla badanego
slebetman

Odpowiedzi:

74

Z mojego zrozumienia process.envmożna po prostu traktować ją jak każdą inną zmienną podczas ustawiania jej właściwości. Pamiętaj jednak, że każda wartość w process.envmusi być ciągiem. Jeśli więc potrzebujesz określonej wartości w swoim teście:

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

Aby uniknąć wycieku stanu do innych testów, należy zresetować zmienną do jej pierwotnej wartości lub całkowicie ją usunąć:

   afterEach(() => {
       delete process.env.NODE_ENV;
   });
Joshua Dutton
źródło
8
Mi to pasuje. Należy pamiętać, że jeśli testujesz moduł odczytujący NODE_ENV, gdy moduł ładuje się po raz pierwszy, prawdopodobnie będziesz chciał ustawić NODE_ENV przed załadowaniem modułu (tj. NODE_ENV można ustawić w bloku beforeEach). Może się to wydawać oczywiste , ale już wcześniej o to mnie chodziło.
Terrence,
Jeśli masz problemy, czy możesz opublikować fragment kodu dla kogoś do obejrzenia? Napisałem swoją odpowiedź mając na uwadze składnię testera Mocha, ale powinna ona działać również z każdym innym biegaczem (np. Laboratorium).
Joshua Dutton,
2
To działa, ale podczas używania znalazłem dziwactwo jest. W moim kodzie produkcyjnym przypisałem z env do const (np const X = process.env.X.). Stała została zadeklarowana w zakresie modułu (ES), a nie w zakresie funkcji. Moje testy zawsze jest --watchkończyły się pomyślnie przy ponownym uruchomieniu testów, ale zawsze kończyły się niepowodzeniem przy pierwszym uruchomieniu. Występuje problem z zamówieniem, którego nie do końca rozumiem. Po prostu upewnij się, że zawsze czytasz bezpośrednio z process.envkodu produkcyjnego (tj. W funkcji) i nie przechowujesz go w pamięci podręcznej na poziomie modułu.
Jesse Buchanan
1
działa to dobrze, jeśli oceniasz process.env w funkcji, ale nie, jeśli jest to stała. na przykład const myValue = process.env.value ? process.env.value : 'default'nie działałoby, jeśli ustawisz wartość process.env.value w teście. Jednak const myValue = () => (process.env.value ? process.env.value : 'default') działa zgodnie z oczekiwaniami!
Rafael Marques
W tym samym duchu miałem: const SWITCH_ON = (process.env.SWITCH_ON.toLowerCase() === 'true');co nie zadziałało, więc zmieniłem to na dwie linie: var switchOn = process.env.SWITCH_ON; const SWITCH_ON = (switchOn === undefined ? false : switchOn.toLowerCase() === 'true');Początek ciągle dawał mi undefinedbłędy w miejscu, w którym robiłem.toLowerCase()
Scala Enthusiast
25

Udało mi process.envsię zostać poprawnie usuniętym w moich testach jednostkowych, klonując go i przywracając metodą porzucenia.

Przykład z użyciem Mocha

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

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

...

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

Należy pamiętać, że zadziała to tylko wtedy, gdy plik process.env jest odczytywany tylko w testowanej funkcji. Na przykład, jeśli testowany kod odczytuje zmienną i używa jej w zamknięciu, nie zadziała. Prawdopodobnie unieważniłeś buforowane żądanie, aby to poprawnie przetestować.

Na przykład następujące elementy nie będą miały kodu pośredniego env:

const nodeEnv = process.env.NODE_ENV;

const fnToTest = () => {
   nodeEnv ...
}
pllee
źródło
3
Ten proces w większości działał. Musiałem dostosować metodę „po”. after(() => { process.env = Object.assign({}, env); }); W przeciwnym razie testy manipulowałyby udostępnioną kopią. Po każdym teście należy ustawić nową wersję.
Kyle
1
@Kyle .. nie, nie byłoby? zakładając, że raz ustawisz środowisko env na początku swojego pliku, zostanie ono przywrócone do tego, co było na początku zestawu testów ..
Więzień
4

W spec-helper.coffeepodobnym przypadku, w którym konfigurujesz piaskownicę sinon, śledź oryginał process.envi przywracaj go po każdym teście, aby nie wyciekać między testami i nie musisz pamiętać o resetowaniu za każdym razem.

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

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

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

W swoim teście używaj process.envjak zwykle.

it 'does something based on an env var', ->
    process.env.FOO = 'bar'
Andrew Homeyer
źródło
underscore„s clonefunkcja działa w miejscu cloneDeep- przydatna, jeśli używasz już underscoreraczej niż lodash.
Rob
4

Za pomocą sinon możesz wyciąć dowolną zmienną taką jak ta.

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

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

 myObj.example; // 'newValue'

Ten przykład jest dokumentacją w formie sinon. https://sinonjs.org/releases/v6.1.5/stubs/


Mając tę ​​wiedzę, możesz odgiąć dowolną zmienną środowiskową. W twoim przypadku wyglądałoby to tak:

 let stub = sinon.stub(process.env, 'FOO').value('bar');
Getriax
źródło
5
Otrzymałem błąd „Nie można usunąć nieistniejącej własnej właściwości FOO”. Używam również wallaby.js do uruchamiania moich testów.
Will Lovett
1
Dziękujemy za opublikowanie odpowiedzi na pytanie „Jak wygląda obcinanie zmiennej env?” zamiast po prostu mówić, że nie musimy, ponieważ możemy nimi manipulować ręcznie :)
Będzie
Miałem ten sam błąd, co @WillLovett i rozwiązałem go, dodając wywołanie wymagania w górnej części skryptu testu jednostkowego: require('dotenv').config();zdałem sobie sprawę, że zwykle jest ono wywoływane, gdy moja aplikacja jest uruchomiona, ale jeśli wykonuję testy jednostkowe bezpośrednio, to brakowałoby wymaganego oświadczenia.
Von Pittman
4

Jak szybko mockować process.env podczas testów jednostkowych.

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

Ale co z właściwościami, które mogą nie istnieć w process.env przed testem? Możesz użyć następującego pakietu, a następnie będziesz mógł przetestować nieistniejące zmienne env.

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

Hossein Rabizadeh
źródło
To nie zadziała, jeśli process.env.USERnie ma już wartości.
Sohail Si
0

Możesz użyć tego, jeśli chcesz odgiąć klucz, którego nie ma w process.env

const sinon = require('sinon')
let sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({ 'SOME_KEY': 'SOME_VALUE' });
Mayank Chaurasia
źródło