为什么不能用Ruby声明方法中的常量?
考虑以下情况,StubFoo是Foo的存根,我希望将其用于某些测试。
class Runner def run Foo = StubFoo foo = Foo.new # using Foo... end end
这会生成以下错误消息: Dynamic constant assignment
然而,在RSpec我可以做以下工作,这是有效的,并且是完全合法的:
it "should be an example" do Foo = StubFoo foo = Foo.new foo.to_s.should == "I am stubbed!" end
关于这一点的一些问题。
- 为什么这适用于RSpec测试用例,而不是上述方法?
- 据我所知,“它”只是RSpec中的一种方法,但我能够在“方法”中重新声明一个常量。
我在使用模拟框架之前做这个,纯粹想要知道如何在Ruby中进行模拟,存根等等。 我听说动态语言更容易模拟/存根,互联网上有指南,其中简单的类重新分配如上所述。 根据我的研究,在Ruby中,不可能在方法中声明常量,但我如上所述会感到困惑。
编辑
对,这开始变得更有意义了。 我已经更新了run,现在正在使用const_set。
def run old = Foo self.class.const_set(:Foo, StubFoo) foo = Foo.new puts foo.to_s self.class.const_set(:Foo, old) foo = Foo.new puts foo.to_s end
然而,这会产生一个警告,那么在Ruby中,模拟框架是什么/如何工作? 显然更优雅,function齐全,但他们只是压制这个警告?
您不能使用Constant = value
在方法定义中重新分配常量。 但是,您可以使用const_set
重新分配它们。 基本上它意味着阻止,但不是禁止,动态不断重新分配。
至于为什么它适用it
:是的, it
是一种方法,但你没有定义,你在称它。 重新分配发生在作为参数传递给it
的块内。
Blocksinheritance创建它们的上下文的类型。 这意味着在块内部, self.class.name
将是您的测试类的名称。 因此,当您在传递给it
方法的块内定义一个常量时,实际上是在测试类上定义一个常量。
因为在您的测试中,您没有定义方法,而是调用 it
。
Ruby检测到您在方法中定义了一个常量,可以多次运行,因此警告。 如果您不止一次调用该方法,您将收到warning: already initialized constant Foo
警告。 这是因为它们应该是常数。 (例如,如果你定义了Foo = Time.now
会发生什么?)
您收到错误,因为如果调用Runner#run
, Foo
将在调用者上意外更改。 例如:
class C; end def add_two_to(x) # this method is defined externally, and you cannot change it. C = Class.new {} # error x + 2 end x = C.new puts add_two_to(5) y = C.new x.is_a? y.class # would be false if the code get here
add_two_to
的调用者不希望重新定义C
更有意义的是,上面代码的最后一行始终是真的。 在你的第二个例子中,你的“方法”实际上不是一个方法,而是一个块。 如果您运行该块两次( it
不会这样做),您将收到此错误:
warning: already initialized constant Q