schalkneethling
schalkneethling

Reputation: 6724

Testing JavaScript without exports using the Jest testing framework

I am in the process of moving an old JS codebase to modern JS. First step though is to add some tests to the current codebase. The current code base is essentially a bunch of individual JS files each wrapped in an IIFE.

This in itself is a problem for tests because, unless something is exposed to the global object, you cannot reach into the IIFE. Some of the code I am refactoring to be simple JS object with properties, which is attached to a namespace(namespace below is just a placeholder name) on the global object, for example:

var namespace = window.namespace || {};
var paymentsHandlerUtils = {
    getNewValue: function(selectedAmount) {
        'use strict';
        return selectedAmount < 1 || isNaN(selectedAmount)
            ? ''
            : '$' + selectedAmount;
    },
    getSelectedAmount: function(value) {
        'use strict';
        return value % 1 === 0 ? parseInt(value) : parseFloat(value).toFixed(2);
    }
};

namespace.paymentsHandlerUtils = paymentsHandlerUtils;

My question is, how would you go about testing this with Jest? I have tried requiring the above as follows:

const paymentsHandlerUtils = require('../js/components/payments/payments-handler-utils.js');

This runs, but the paymentsHandlerUtils object is just an empty {}. Not surprising, as nothing is being returned by simply executing the JS. However, window.namespace is also undefined. Seems like the code is not being executed in the context of jsDOM, so the global(s) is not created.

Is there a way to get this to work, or is this simply not a use case for Jest? Thanks in advance.

Upvotes: 4

Views: 625

Answers (2)

YakovL
YakovL

Reputation: 8375

In fact this is now possible with rewire. Here's what you have to do:

  1. install rewire (npm i rewire)

  2. in your jest file,

    const rewire = require("'rewire');
    const paymentsHandlerUtilsRewire = rewire('../js/components/payments/payments-handler-utils.js');
    const namespace = paymentsHandlerUtilsRewire.__get__('namespace');
    
  3. do what you were planning to do with namespace and namespace.paymentsHandlerUtils

Read more in the rewire repo; there's also babel-plugin-rewire for ES6+ (see, for instance, here).

Upvotes: 0

Andr&#233;a Maugars
Andr&#233;a Maugars

Reputation: 425

I don’t think there is a way to access module globals when imported as it goes against the principle of encapsulated modules in the first place.

An alternative which would require the least refactoring would be to add the following code to all your modules:

if (typeof exports === "object") {
    module.exports = paymentsHandlerUtils;
}
namespace.paymentsHandlerUtils = paymentsHandlerUtils;

It is inspired by the old UMD (Universal Module Definition). The condition detects if you are running in a commonjs environment and exports your variable. You will then be able to require it in your tests:

const paymentsHandlerUtils = require('../js/components/payments/payments-handler-utils.js');

Otherwise you need to use another test suite that works in the browser, because Jest doesn’t.

Good luck to your migrations!

Upvotes: 5

Related Questions