nodejs/mocha/chai as promise : variables used in expected async function initialized outside

480 views Asked by At

I am brand new to mocha/chai and I spent 2 days trying to solve the following issue without any success (please note that the code below is just to present the concept, it is not the real one).

I have got a JS file called "api.js" in which some variables such as SERVER_URL are initialized at the top of the file through dotenv framework.

api.js :

const SERVER_URL = process.env.SERVER_URL;

async function startAPI () {
  return new Promise ( (resolve, reject) => {
                                             console.log(`${SERVER_URL}`);
                                             resolve();
                                            });
exports = {startAPI};

Now I have got "test.js" file in which :

test.js:

require('../api');

it('the test', async () => {
  return await expect(api.startAPI()).to.be.fulfilled;
});

The problem is that SERVER_URL is undefined during the test and I cannot modify the api.js (as I am not the owner), just the test.js.

How can I run the test with the SERVER_URL variable set correctly (to process.env.SERVER_URL value from api.js) ?

Is there a solution without any refactoring ?

And if not what is the best solution ?

Experts, thanks in advance for your precious help

3

There are 3 answers

0
Estus Flask On BEST ANSWER

A way to improve testability is to use process.env.SERVER_URL instead of SERVER_URL where possible - or getServerUrl():

const getServerUrl = () => process.env.SERVER_URL;

This way process.env.SERVER_URL can be mocked at any point.

An alternative is to import module after process.env.SERVER_URL was mocked. This should involve decaching if there's more than one test that uses this module, because it won't be re-evaluated otherwise.

const decache = require('decache');

...
let originalServerUrl;

beforeEach(() => {
  originalServerUrl = process.env.SERVER_URL;
});

beforeEach(() => {
  process.env.SERVER_URL = originalServerUrl;
});

it('the test', async () => {
  decache('../api');
  process.env.SERVER_URL = '...';
  const api = require('../api');
  await expect(api.startAPI()).to.be.fulfilled;
});

If it's expected that there's no SERVER_URL in tests, it can be just discarded after it was mocked:

0
phts On

The easiest way would be just to set these variables when you run your test from CLI:

e.g. in npm scripts:

"scripts": {
  "test": "SERVER_URL='http://example.com' mocha"
}

or directly from terminal:

$ SERVER_URL='http://example.com' npm test

But better solution would be mock environment variables in your tests with little refactoring. And need proxyquire to be installed. And actually async/await is not needed here.

const proxyquire = require('proxyquire').noPreserveCache() // noPreserveCache is important to always have refreshed script with new process.env.SERVER_URL in each test

const MOCKED_SERVER_URL = 'http://example.com'

describe('example', () => {
  let initialServerUrl
  let api

  beforeEach(() => {
    initialServerUrl= process.env
  })

  afterEach(() => {
    process.env = initialServerUrl
  })

  it('fulfilled', () => {
    process.env.USE_OTHER_CODE_PATH = MOCKED_SERVER_URL 
    api = proxyquire('../api', {})
    return expect(api.startAPI()).to.be.fulfilled
  })

  it('rejected', () => {
    process.env.USE_OTHER_CODE_PATH = ''
    api = proxyquire('../api', {})
    return expect(api.startAPI()).to.be.rejected
  })
})
0
Luiz Chagas Jr On

You can set .env variables with mocha using the following line:

env SERVER_URL=htt://api.yourserver.com/ mocha test

This way mocha knows what to expect from your process.env.SERVER_URL