测试IIFE(立即调用函数表达式)的最佳方法

6

我有一个现有的应用程序,在浏览器中广泛使用IIFE。我试图将一些单元测试引入到代码中,并保持对代码库新更新的IIFE模式。但是,我甚至都无法编写一个能够访问该代码的测试。例如,我在整个代码库中都看到了这种类型的逻辑:

var Router = (function (router) {

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

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

然后,在标记中使用 script 标签包含 JS 文件:
<script src="js/RouteUser.js"></script>

在生产代码中,它被这样称为:

Router.routeUser(myUser)

我的问题是,如何编写一个测试来测试方法routeUser?我在Mocha测试中尝试了以下代码:

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")
        }
    });
});

但是我遇到了一个异常:
TypeError: router.routeUser is not a function
at Context.<anonymous> (src\test\js\RouteUser.test.js:8:20)

然后我尝试返回该方法,但仍然出现相同的错误:

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

有人能指引我正确的方向吗?


你正在尝试 require 这个文件,它是否有任何 module.exports - Kos
不,我没有导出。我想我可以尝试添加它,但我试图不改变生产代码中现有的模式。module.exports难道不只是针对Node吗?在这种情况下,代码在浏览器中运行,因此不是技术上的Node,所以我避免使用module.exports。你有什么想法? - Ian Michel
你提到了Mocha,甚至在你的代码片段中使用了require()。这已经有点暗示你想使用Node运行单元测试了。但是,是在浏览器中运行Mocha呢还是在Node中呢? - Kos
我现在打算尝试在浏览器中运行这些测试。我甚至不知道这是可能的,这将对我有很大帮助,因为我不需要修改现有的代码。 - Ian Michel
2个回答

6

看起来...

  • 您有一些只在浏览器上下文中使用的代码库(IIFE 的使用表明这一点)
  • 您想引入无浏览器单元测试(使用 Jest、Mocha 等)并使用 node.js(好主意!)
  • 但是您可能不想在这个时候将整个代码库迁移到不同的编码风格(根据代码库的大小可能会很费力)

在这些假设的基础上,问题在于您希望您的代码...

  • 在生产中使用时作为

1
哇,工作了,谢谢!不过我不明白什么是"typeof exports === "object""。我从未在我的IIFE中定义"exports"。它从哪里来?另外,对于module.exports,module是在哪里定义的?至于长期解决方案,重写所有代码仅使用模块。我认为上面的Router本身就是一个模块,因为它是一个可以在浏览器中使用Router.routeUser()调用的独立代码块。你说的模块是什么意思?当你说这个时,你是指Router应该始终具有module.exports,即使typeof exports!="object"吗? - Ian Michel
通过模块,我是指CommonJS模块——源文件可以通过require()导入东西,也可以通过分配给module.exports来导出东西。Node(和mocha)默认使用CommonJS,但构建工具,如Webpack也能理解它。如果它们是CommonJS,这使得在浏览器和Node之间重复使用模块变得容易。(还有使用importexport关键字的ES6模块。) - Kos
typeof exports === "object" 只是检查作用域中是否定义了符号“exports” - 如果有,我们假设代码正在运行在 CommonJS 上下文中。你现在想让相同的代码在浏览器中运行而不使用构建工具,对吧?这就是为什么我们需要检查我们所处的环境。 - Kos

1
如果您想在浏览器中运行Mocha测试,无需更改现有代码。
让我们来看一下IIFE模式,因为根据您的代码,我认为您可能误解了它的工作原理。基本形状如下:
var thing = (function() {

   return 1;

})();

console.log(thing) // '1'

这是一个var声明,将thing设置为等号右侧的值。在右侧,第一组括号包含一个函数。然后,第二组括号紧挨着它,在末尾。第二组括号调用包含在第一组括号中的函数表达式。这意味着函数的return值将是var语句中的右侧值。因此,thing等于1
在您的情况下,这意味着外部的Router变量被设置为您的函数返回的router变量。这意味着在将脚本包含在DOM中后,您可以在测试中使用Router来访问它。
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'
        }
    });
});

如果你想使用node来运行测试,请参考Kos的回答。

有趣。感谢您对IIFEs的解释。我没有想过在浏览器中运行我的测试,这确实意味着我今天可以像生产代码一样访问路由器。事实上,这样运行测试非常有意义,因为它模拟了代码在生产环境中的运行方式。谢谢。 - Ian Michel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接