Search code examples
javascriptunit-testingmocha.jsiife

Best way to test IIFE (Immediately Invoked Function Expression)


So I have an existing application which uses IIFEs extensively in the browser. I'm trying to introduce some unit testing into the code and keep with the pattern of IIFE for new updates to the code base. Except, I'm having trouble even writing a test which gives me a handle to the code. For example I see this type of logic all over the code base:

var Router = (function (router) {

   router.routeUser = function(user) {
      console.log("I'm in! --> " + user)
   };

   return router;
})(Router || {});

Then the JS file is included in a script tag in the markup:

<script src="js/RouteUser.js"></script>

and called like this in the production code:

Router.routeUser(myUser)

So my question is, how do I write a test which tests the method routeUser? I've tried this in my Mocha Test:

var router = require('../../main/resources/public/js/RouteUser');

suite('Route User Tests', function () {
    test('Route The User', function () {
        if (!router)
            throw new Error("failed!");
        else{
            router.routeUser("Me")
        }
    });
});

But I get an exception:

TypeError: router.routeUser is not a function
at Context.<anonymous> (src\test\js\RouteUser.test.js:8:20)

Then I tried returning the method, which gives the same error:

var Router = (function (router) {
    return {
        routeUser: function (user) {
            console.log("I'm in! --> " + user)
        } 
    }
}
)(Router || {});

Can anyone point me the right direction here?


Solution

  • If you intend to run your Mocha tests in the browser, you do not have to alter your existing code.

    Let's walk through the IIFE pattern, because based on your code, I think you may misunderstand how it works. The basic shape is this:

    var thing = (function() {
    
       return 1;
    
    })();
    
    console.log(thing) // '1'
    

    It's a var declaration setting thing equal to the value on the right side of the equals sign. On the right, the first set of parens wraps a function. Then, a second set of parens sits right next to it, at the end. The second set invokes the function expression contained in the first set of parens. That means the return value of the function will be the right-side value in the var statement. So thing equals 1.

    In your case, that means that the outer Router variable is set equal to the router variable returned by your function. That means you can access it as Router in your tests, after including the script in the DOM:

    suite('Route User Tests', function () {
        test('Route The User', function () {
            if (!Router) // <- Notice the capital 'R'
                throw new Error("failed!");
            else {
                Router.routeUser("Me") // <- capital 'R'
            }
        });
    });
    

    If you intend to run your tests with node, see Kos's answer.