admin管理员组

文章数量:1312662

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?

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?

Share Improve this question edited Nov 9, 2017 at 16:23 Ian Michel asked Nov 9, 2017 at 14:40 Ian MichelIan Michel 5696 silver badges9 bronze badges 4
  • you're trying to require this file, does it have any module.exports? – Kos Commented Nov 9, 2017 at 16:48
  • No I don't have exports. I suppose I could try adding it, but I'm trying not to alter the existing patter in my production code. Aren't module.exports only for Node? In this case the code is run in the browser so isn't technically Node, so I am shying away from module.exports. Thoughts? – Ian Michel Commented Nov 9, 2017 at 16:56
  • You mentioned Mocha and even used require() in your code snippet. This already kind of suggests that you want to run your unit tests using Node. But yeah, Mocha can run in a browser too - which is it? – Kos Commented Nov 9, 2017 at 17:00
  • I'm going to try and run these tests in the browser for now. I didn't even know this was possible, and it would help me greatly as I wouldn't have to alter existing code I think. – Ian Michel Commented Nov 9, 2017 at 17:43
Add a ment  | 

2 Answers 2

Reset to default 6

It sounds that...

  • you have a codebase of scripts that are only used in the browser context (usage of IIFE suggests this)
  • you'd like to introduce browserless unit tests (Jest, Mocha?) using node.js (good idea!)
  • but you probably don't want to migrate the whole codebase to a different coding style at this moment in time (can be a lot of work depending on the size of your codebase)

Given these assumptions, the problem is that you want your code to...

  • act as a script when used on production (set global window.Router etc)
  • act as a module when used in unit tests so that you can require() it in unit tests

UMD

UMD, or universal module definition, used to be a mon way to write code so that it can work in multiple environments. Interesting approach, but very cumbersome, and I like to think UMD is a thing of the past nowadays...

I'll leave it here for pleteness.

Just take UMD's idea

If the only thing you want for now to make a specific script act as a module too, so that it's importable in tests, you could do a small tweak:

var Router = (function (router) {

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

   if (typeof exports === "object") {
      module.exports = router;
      // now the Mocha tests can import it!
   }
   return router;
})(Router || {});

Long term solution

In the long run, you can get lots of benefits by rewriting all your code to use ONLY modules and use a tool like webpack to package it for you. The above idea is a small step in your direction that gives you one specific benefit (testability). But it is not a long term solution and you'll have some trouble handling dependencies (what if your Router expects some globals to be in place?)

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.

本文标签: javascriptBest way to test IIFE (Immediately Invoked Function Expression)Stack Overflow