当我要将模块包含在Minitest / spec测试中时,可以从模块访问功能,但不能访问其中定义的类。示例:
module Foo
def do_stuff
end
class Bar
end
end
x=describe Foo do
include Foo
end
p x.constants # shows :Bar
describe Foo do
include Foo
it "foos" do
do_stuff # works
Bar.new # raises a NameError
end
end
运行此代码段将为我提供“ NameError:未初始化的常数Bar”,但是p x.constants
显示已定义Bar
。我查看了describe
的Minitest源代码,它在某个匿名类的上下文中在块上使用了class_eval
。当我在普通类的上下文中执行此操作时,它可以正常工作,并且我可以访问Bar
。为什么它不能与describe/it
一起使用,或者我必须做什么才能直接访问类?
编辑:
有趣的是,如果您直接在某个类上调用class_eval
,则可以找到包含的类Bar
,例如
class Quux
def it_foos
do_stuff # works
Bar.new # does NOT raise a NameError
end
end
Quux.class_eval do
include Foo
end
Quux.new.it_foos
不会抛出NameError
...
答案 0 :(得分:1)
如果您查看#class_eval
(例如,https://ruby-doc.org/core-2.5.0/Module.html#method-i-class_eval)的文档,您将在此处看到答案:“在mod的上下文中评估字符串或块,除非给出块,不影响常量/类变量的查找”。
因此,包含在class_eval
中根本不会影响常量解析。
据我从对minitest源代码的简短了解中可以了解,describe
在内部创建了一个新的匿名类(将其命名为C
),并使用您提供的代码块将class_eval强制转换为该类。在此调用期间,it
创建各自的测试实例方法,这些方法随后将执行。但是include
不会影响C
的常量解析度,因此Bar
仍然是未知的。
有一个显而易见的(而且很丑陋的)解决方案-以下应该可行,因为您将Foo
包含在外部上下文中,因此Bar进入了describe
可访问的词法范围:
include Foo
describe Foo do
it "foos" do
do_stuff
Bar.new
end
end
但是,我会避免这样的代码。可能最好显式设置类模拟,例如
module Foo
def do_stuff
"foo"
end
class Bar
def do_stuff
"bar"
end
end
end
...
describe Foo do
let(:cls) { Class.new }
before { cls.include(Foo) }
it "foos" do
assert cls.new.do_stuff == "foo"
end
it "bars" do
assert cls::Bar.new.do_stuff == "bar"
end
end
(但是请带后者吃些盐-我几乎从不使用Minitest,所以不知道它的“常见习语”)