测试 IIFE(立即调用的函数表达式)的最佳方法
Best way to test IIFE (Immediately Invoked Function Expression)
所以我有一个在浏览器中广泛使用 IIFE 的现有应用程序。我试图在代码中引入一些单元测试,并与 IIFE 的模式保持一致,以便对代码库进行新的更新。除了,我什至无法编写一个测试来处理代码。例如,我在代码库中看到了这种类型的逻辑:
var Router = (function (router) {
router.routeUser = function(user) {
console.log("I'm in! --> " + user)
};
return router;
})(Router || {});
然后JS文件包含在标记中的脚本标签中:
<script src="js/RouteUser.js"></script>
并在生产代码中这样调用:
Router.routeUser(myUser)
所以我的问题是,如何编写测试方法 routeUser 的测试?我在我的摩卡测试中尝试过这个:
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 || {});
谁能给我指出正确的方向?
听起来...
- 您有一个 脚本代码库 仅在浏览器上下文中使用(使用 IIFE 表明这一点)
- 您想使用 node.js(好主意!)
引入无浏览器单元测试(Jest、Mocha?)
- 但您现在可能不想将整个代码库迁移到不同的编码风格(可能需要大量工作,具体取决于您的代码库的大小)
鉴于这些假设,问题是您希望您的代码...
- 在生产中使用时作为 脚本(设置全局
window.Router
等)
- 在单元测试中用作模块,以便您可以在单元测试中
require()
它
UMD
UMD,即通用模块定义,曾经是一种通用的编写代码的方式,以便它可以在多种环境中工作。有趣的方法,但非常麻烦,我想认为 UMD 现在已经成为过去...
I'll leave it here for completeness.
就拿UMD的idea来吧
如果您现在只想让特定脚本也充当模块,以便它在测试中可导入,您可以做一些小调整:
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 || {});
长期解决方案
在漫长的 运行 中,您可以通过重写所有代码以仅使用模块并使用 webpack
之类的工具为您打包来获得很多好处。上面的想法是朝着你的方向迈出的一小步,它给你一个特定的好处(可测试性)。但这不是一个长期的解决方案,你会在处理依赖关系时遇到一些麻烦(如果你的路由器希望一些全局变量到位怎么办?)
如果您打算 运行 在浏览器中进行 Mocha 测试,则不必更改现有代码。
让我们了解一下 IIFE 模式,因为根据您的代码,我认为您可能误解了它的工作原理。基本形状是这样的:
var thing = (function() {
return 1;
})();
console.log(thing) // '1'
这是一个var
声明设置thing
等于等号右边的值。在右侧,第一组 parens 包装了一个函数。然后,第二组 parens 就在它旁边,最后。第二组 调用 第一组 parens 中包含的函数表达式。这意味着函数的 return
值将是 var
语句中的 right-side 值。所以 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'
}
});
});
如果您打算 运行 使用节点进行测试,请参阅 Kos 的回答。
所以我有一个在浏览器中广泛使用 IIFE 的现有应用程序。我试图在代码中引入一些单元测试,并与 IIFE 的模式保持一致,以便对代码库进行新的更新。除了,我什至无法编写一个测试来处理代码。例如,我在代码库中看到了这种类型的逻辑:
var Router = (function (router) {
router.routeUser = function(user) {
console.log("I'm in! --> " + user)
};
return router;
})(Router || {});
然后JS文件包含在标记中的脚本标签中:
<script src="js/RouteUser.js"></script>
并在生产代码中这样调用:
Router.routeUser(myUser)
所以我的问题是,如何编写测试方法 routeUser 的测试?我在我的摩卡测试中尝试过这个:
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 || {});
谁能给我指出正确的方向?
听起来...
- 您有一个 脚本代码库 仅在浏览器上下文中使用(使用 IIFE 表明这一点)
- 您想使用 node.js(好主意!) 引入无浏览器单元测试(Jest、Mocha?)
- 但您现在可能不想将整个代码库迁移到不同的编码风格(可能需要大量工作,具体取决于您的代码库的大小)
鉴于这些假设,问题是您希望您的代码...
- 在生产中使用时作为 脚本(设置全局
window.Router
等) - 在单元测试中用作模块,以便您可以在单元测试中
require()
它
UMD
UMD,即通用模块定义,曾经是一种通用的编写代码的方式,以便它可以在多种环境中工作。有趣的方法,但非常麻烦,我想认为 UMD 现在已经成为过去...
I'll leave it here for completeness.
就拿UMD的idea来吧
如果您现在只想让特定脚本也充当模块,以便它在测试中可导入,您可以做一些小调整:
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 || {});
长期解决方案
在漫长的 运行 中,您可以通过重写所有代码以仅使用模块并使用 webpack
之类的工具为您打包来获得很多好处。上面的想法是朝着你的方向迈出的一小步,它给你一个特定的好处(可测试性)。但这不是一个长期的解决方案,你会在处理依赖关系时遇到一些麻烦(如果你的路由器希望一些全局变量到位怎么办?)
如果您打算 运行 在浏览器中进行 Mocha 测试,则不必更改现有代码。
让我们了解一下 IIFE 模式,因为根据您的代码,我认为您可能误解了它的工作原理。基本形状是这样的:
var thing = (function() {
return 1;
})();
console.log(thing) // '1'
这是一个var
声明设置thing
等于等号右边的值。在右侧,第一组 parens 包装了一个函数。然后,第二组 parens 就在它旁边,最后。第二组 调用 第一组 parens 中包含的函数表达式。这意味着函数的 return
值将是 var
语句中的 right-side 值。所以 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'
}
});
});
如果您打算 运行 使用节点进行测试,请参阅 Kos 的回答。