在很多框架中,内部函数变量被用作私有(private)变量,例如
Raphael = (function(){
var _private = function(a,b) {return a+b;};
var _public = function(a) {return _private(a,a);}
var object = {mult2:_public};
return object;
})();
在这里,我们无法从全局命名空间访问名为private 的变量,因为它是第一行匿名函数的内部变量。
有时这个函数包含一个大的 Javascript 框架,这样它就不会污染全局命名空间。
我需要对 Raphael 内部使用的某些对象进行单元测试(在上面的示例中,我希望对对象 private 运行单元测试)。我如何测试它们?
编辑:我收到了关于应该测试公共(public)接口(interface)的单元测试的评论。
让我指定一个用例。我正在编写一个名为 Raphael 的库。该库应该只向全局命名空间添加一个名称,仅此而已。这是 Javascript 的特殊要求,因为 Javascript 没有命名空间。
假设 Raphael 使用链表。如果 Javascript 有包的概念,我会这样做
require 'linked_list'
Raphael = (function(){/* use linked list */})();
但是 Javascript 不允许我以任何不会污染链接列表对象的全局范围的方式来做到这一点!因此,我必须将 linked_list 内联到 Raphael 的本地范围内:
Raphael = (function(){
/* implement linked list */
var linked_list = function(){/*implementation*/};
})();
现在我想测试 linked_list 的实现。
最佳答案
您仍然没有捕获要点。
单元测试的目的是验证对象的公共(public)接口(interface)是否按照预期进行。单元测试显示代码如何工作。
唯一应该测试的是对象的公共(public)接口(interface)。这样,当您作为开发人员想要更改对象的实现方式时,您只需担心被测试的对象是否仍然按预期运行。
如果您觉得闭包内部的对象需要测试,那么就进行测试,但在外部进行,然后将其传递到闭包中。
var Raphael= function(listIterator) {
listIterator.method();
}(new ListIterator());
如下所示的虚假 hack 是完全不合适的(在单元测试或任何地方)。
测试函数应该简单,只测试一件事,并且有一个断言。这通常可以在三到十行测试代码中发生。
当您到达测试函数复杂的地步时,因为它们将遵循您所询问的方法,那么要么 (1) 意识到你的设计可能不是你想要的,然后改变它,或者 (2) 改变你在测试中的期望。
关于您发布的代码,您忘记了var,漏掉了一个分号,并使用了两个保留字作为标识符:private 和public。
不使用 var 的后果是可能触发与非标准 GlobalScopePolluter 类型对象的各种实现相关的错误和问题(“对象不支持此属性或方法”在 IE 中看到)。使用 FutureReservedWord 的结果是 SyntaxError。实现可能会提供语法扩展以允许 FutureReservedWord 作为标识符,实际上很多人都这样做,但是最好不要依赖此类扩展,如果您遇到错误,那将完全是您的错。
您提到向用户交付代码。我建议你在获得更多经验和对你正在做的事情的理解之前不要这样做。
// DO NOT USE THIS CODE.
var Raphael = (function(){
var _private = function(a,b) {return a+b;};
var _public = function(a) {return _private(a,a);};
var object = {mult2:_public};
return object;
})();
var leakedFunction;
// Spurious hack:
// Give valueOf a side effect of leaking function.
// valueOf is called by the _private function as a
// side effect of primitive conversion, where
// ToPrimitive(input argument, hint Number) results
// in calling valueOf.
function valueOfSnoop(){
leakedFunction = leakedFunction || valueOfSnoop.caller || function(){};
return 2;
}
var a = {
valueOf : valueOfSnoop
};
Raphael.mult2(a, 3);
var privateMathod = leakedFunction;
alert(leakedFunction(1, 2));
该示例代码只是为了证明这种事情是可能的。鉴于选择,它是前面提到的替代方案的糟糕替代品;要么改变你的设计,要么改变你的测试。
关于javascript - 在 Javascript 中访问内部函数变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2775811/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。