我是TDD的新手,我正在尝试编写方法以在每个类的基础上生成URL,这些URL使用Sinatra和Sinatra-Contrib中的命名空间库从父类继承。我走得不太远,因为我得到了一个失败的 RSpec 测试,这使我无法进一步前进:未定义的方法"命名空间"。谁能帮忙?
宝石文件:
source 'https://rubygems.org'
ruby '1.9.3'
gem 'sinatra'
gem 'sinatra-contrib'
gem 'rack'
gem 'thin'
group :development, :test do
gem 'rspec'
gem 'rack-test'
gem 'ZenTest'
gem 'autotest-growl'
gem 'autotest-fsevent'
end
base_model.rb:
require 'sinatra'
require 'sinatra/namespace'
require 'rack'
def generate_routes_for_model(model_class, rootUrl)
namespace rootUrl do
get '/show' do
"I'm the model's show route"
end
end
end
base_model_spec.rb
require_relative '../base_model.rb'
require 'rack/test'
set :environment, :test
class Foo
end
def app
Sinatra::Application
end
include Rack::Test::Methods
describe 'Create Routes for test Class' do
it "should load foo.show" do
generate_routes_for_model(Foo, '/foo')
get '/foo/show'
last_response.should be_ok
end
end
以下是测试结果:
失败:
1) Create Routes for test Class should load foo.show
Failure/Error: generate_routes_for_model(Foo, '/foo')
NoMethodError:
undefined method `namespace' for #<RSpec::Core::ExampleGroup::Nested_2:0x007f8571102e00>
# ./base_model.rb:16:in `generate_routes_for_model'
# ./spec/base_model_spec.rb:24:in `block (2 levels) in <top (required)>'
一看,这似乎是一个范围问题。 namespace
是Sinatra扩展的类方法(这就是为什么您在class
定义中而不是在实例方法中调用它的原因(。
当您运行 RSpec 时,namespace
是否曾经在继承自Sinatra::Base
的类中?我不这么认为。你需要提供一个namespace
可以在其中运行的Sinatra类。例如
class Foo; end
app = Sinatra.new do
register Sinatra::Namespace
end
def generate_routes_for_model(app_class, model_class, rootUrl)
['show'].each do |route|
app_class.namespace rootUrl do
get route do
"I'm the model's show route"
end
end
end
end
generate_routes_for_model app, Foo, "/"
app.run!
然后去http://localhost:4567/show
会回应"我是模特的秀场路线"。
或者也许使用class_eval
来定义代码,有几种方法可以解决这个问题,要记住的主要事情是确保范围正确,否则应用程序或 RSpec 等将无法找到该方法。
编辑:
此外,我会将生成移动到一个before
块中,因为它是为集成测试而设置的(你实际上是在说"如果路由存在,那么生成工作,我不需要在这里测试方法",所以它是公共 API 的黑盒测试 - 任何其他名称的集成测试:(如果要测试方法本身,那将是一个单元测试,并且需要查看app
以查看是否已生成路由。
describe 'Create Routes for test Class' do
before do
generate_routes_for_model(Foo, '/foo')
get '/foo/show' # this, too, is not a test but a pre-requisite for the test
end
it "should load foo.show" do
last_response.should be_ok
end
end