RSpec 为 /lib 子目录中的代码引发 NameErrors
Posted
技术标签:
【中文标题】RSpec 为 /lib 子目录中的代码引发 NameErrors【英文标题】:RSpec raises NameErrors for code in subdirectory of /lib 【发布时间】:2015-05-20 07:05:23 【问题描述】:我有一个结构如下的 gem 项目:
foo-bar
├── lib
│ └── foo
│ ├── bar
│ │ └── qux.rb
│ └── bar.rb
└── spec
├── spec_helper.rb
└── unit
├── baz_spec.rb
└── qux_spec.rb
在lib/foo
中,bar.rb
定义了一个模块Foo::Bar
,在其中定义了一个类Foo::Bar::Baz
。在lib/foo/bar
、qux.rb
内部定义了一个类Foo::Bar::Qux
。
spec_helper.rb
设置 RSpec 和 Simplecov,并以require 'foo/bar'
结束。 baz_spec.rb
和 qux_spec.rb
都以 require 'spec_helper'
开头。
baz_spec.rb
具有Foo::Bar::Baz
的规格,并且工作正常。但是,具有 Foo::Bar::Qux
规范的 qux_spec.rb
失败并显示:
/Users/me/foo-bar/spec/unit/qux_spec.rb:6:in `<module:Bar>': uninitialized constant Foo::Bar::Qux (NameError)
from /Users/me/foo-bar/spec/unit/qux_spec.rb:4:in `<module:Foo>'
from /Users/me/foo-bar/spec/unit/qux_spec.rb:3:in `<top (required)>'
from /Users/me/.rvm/gems/ruby-2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1226:in `load'
...
(etc.)
我通过将Foo::Bar::Baz
的代码从lib/foo/bar.rb
移出到它自己的文件lib/foo/bar/baz.rb
中验证了这不仅仅是一个错字,之后baz_spec.rb
也停止工作。
我是否将类声明为似乎也没有什么区别
class Foo::Bar::Qux
...
或作为
module Foo
module Bar
class Qux
...
我在 Mac OS X Yosemite 上使用 Ruby 2.2.0 和 RSpec 3.2.2。
很明显,我的requires
有问题,但作为一个 Ruby 新手,我没有看到它。有什么想法吗?
【问题讨论】:
可能的解决方案取决于您使用的框架类型。那么,您使用的是 Rails 还是 ActiveSupport? 两者都不是。好吧,我想 ActiveSupport 在那里,因为我的 gemspec 中有 ActiveJob,但它还没有被使用。 【参考方案1】:您需要在./lib
下添加foo.rb
文件,并为每个文件添加require 语句,以便加载它们。您可以查看示例 gem 以供参考:dogeify 和一篇指导您完成 gem 创建的文章 build your first gem。
【讨论】:
【参考方案2】:如果您使用的是ActiveSupport
,这样的一行代码将对您有所帮助:
ActiveSupport::Dependencies.autoload_paths << "./lib"
如果您现在尝试使用Foo::Bar::Qux
,ActiveSupport 将在lib
文件夹中查找名为foo/bar/qux.rb
的文件。
【讨论】:
我不建议使用 ActiveSupport 自动加载功能,因为它引入了您可能不需要的依赖项,并且也无法控制文件加载的顺序。此外,如果您使用的是最新版本,请考虑使用 ruby 的内置自动加载功能 (ruby-doc.org/core-2.1.0/Module.html#method-i-autoload)【参考方案3】:已解决:在仔细查看了我参与 cargo-culting 的其他项目后,我意识到我误解了 require
。
与(显然)它的 Rails 等效项不同,开箱即用的 Kernel.require
只加载 .rb
文件(和扩展库)。所以在上面的例子中,require 'foo/bar'
不会从foo/bar
目录加载文件,它只是加载foo/bar.rb
。
为了加载foo/bar
下的文件,包括qux.rb
,我必须进入bar.rb
并在模块声明的顶部显式加载这些文件:
module Foo
module Bar
Dir.glob(File.expand_path('../bar/*.rb', __FILE__), &method(:require))
# ...module declaration continues...
我想,这只是等待那些从其他更重量级语言转向 Ruby 的人所面临的众多脚本语言传统陷阱之一。
【讨论】:
以上是关于RSpec 为 /lib 子目录中的代码引发 NameErrors的主要内容,如果未能解决你的问题,请参考以下文章
在某些 RSpec rails 请求示例中测试 HTTP 状态代码,但在其他示例中测试引发异常
Rails 4, RSpec 3.2 - 如何模拟 ActionMailer 的 Deliver_now 方法来引发异常