Rspec:在路由规范中添加一些头请求

我正在开发一个Rails应用程序,它具有JSON格式的REST API并且版本化(根据这个优秀的Ryan演员: http : //railscasts.com/episodes/350-rest-api-versioning )。

例如,有一个spec / requests规范:

require 'spec_helper' describe "My Friends" do describe "GET /my/friends.json" do it "should get my_friends_path" do get v1_my_friends_path, {}, {'HTTP_ACCEPT' => 'application/vnd.myapp+json; level=1'} response.status.should be(401) end end end 

它运作良好。 但是(保持这个例子)我们如何编写路由规范? 例如,这个规范是不正确的:

 require 'spec_helper' describe "friends routing" do it "routes to #index" do get("/my/friends.json", nil, {'HTTP_ACCEPT' => 'application/vnd.myapp+json; level=1'}). should route_to({ action: "index", controller: "api/v1/private/my/friends", format: "json" }) end end 

我尝试了不同的方法(例如request.headers['Accept']@request.headers['Accept'] ,其中request未定义且@request为nil); 我真的不明白该怎么做。

我使用的是Ruby 1.9.3,Rails 3.2.6和rspec-rails 2.11.0。 谢谢。

通过结合Cristophe和Piotr的答案的想法,我想出了一个适合我的解决方案。 我正在使用rspec和rails 3.0。

 it 'should route like i want it to' do Rack::MockRequest::DEFAULT_ENV["HTTP_ACCEPT"] = "*/*" {get: "/foo/bar"}. should route_to( controller: 'foo', action: 'bar', ) Rack::MockRequest::DEFAULT_ENV.delete "HTTP_ACCEPT" end 

目前你不能在路由规范中发送addititional Headers,这是由于actionpack-3.2.5/lib/action_dispatch/routing/route_set.rb中的line 608

 env = Rack::MockRequest.env_for(path, {:method => method}) 

path是您请求的路径"/my/friends.json" ,方法是:get生成的env包含如下内容:

 { "rack.version"=>[1, 1], "rack.input"=>#, "rack.errors"=>#, "rack.multithread"=>true, "rack.multiprocess"=>true, "rack.run_once"=>false, "REQUEST_METHOD"=>"GET", "SERVER_NAME"=>"your-url.com", # if path was http://your-url.com/ "SERVER_PORT"=>"80", "QUERY_STRING"=>"", "PATH_INFO"=>"/", "rack.url_scheme"=>"http", "HTTPS"=>"off", "SCRIPT_NAME"=>"", "CONTENT_LENGTH"=>"0" } 

如果你能够模拟Rack::MockRequest::env_for那么应该可以注入除env_for生成的头之外的其他头文件(参见上面的哈希)。

除了你当前正在使用route_to matcher错误之外,你应该在Hash上调用它,你可以在其中指定方法和路径,如下所示:

 { get: '/' }.should route_to(controller: 'main', action: 'index') 

如果您能够模拟出env_for并让它返回标题,请告诉我们。很高兴知道。

关心Christoph

 before do ActionDispatch::TestRequest::DEFAULT_ENV["action_dispatch.request.accepts"] = "application/vnd.application-v1+json" end after do ActionDispatch::TestRequest::DEFAULT_ENV.delete("action_dispatch.request.accepts") end 

您可以使用rspec的and_wrap_original来模拟Rack::MockRequest.env_for方法:

 expect(Rack::MockRequest).to receive(:env_for).and_wrap_original do |original_method, *args, &block| original_method.call(*args, &block).tap { |hash| hash['HTTP_ACCEPT'] = 'application/vnd.myapp+json; level=1' } end 

对于Rails 3和4,我在一个RSpec周围的钩子中完成了以下操作:

 around do |example| Rack::MockRequest::DEFAULT_ENV['HTTP_ACCEPT'] = 'application/vnd.vendor+json; version=1' example.run Rack::MockRequest::DEFAULT_ENV.delete 'HTTP_ACCEPT' end 

由于Rack >= 2.0.3 ( 由Rails 5使用 ), Rack::MockRequest::DEFAULT_ENV 哈希被冻结 。

您可以重新定义常量并使用Kernel.silence_warnings来消除 Ruby警告:

 around do |example| silence_warnings do Rack::MockRequest::DEFAULT_ENV = Rack::MockRequest::DEFAULT_ENV.dup end Rack::MockRequest::DEFAULT_ENV['HTTP_ACCEPT'] = 'application/vnd.vendor+json; version=1' example.run Rack::MockRequest::DEFAULT_ENV.delete 'HTTP_ACCEPT' end 

这有点像黑客,但它就像一个魅力。