node.jspromiseasync-awaitchaichai-as-promised

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


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


Solution

  • 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: