10

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

例如,有一个规范/请求规范:

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']and @request.headers['Accept'], where requestis undefined and @requestis nil);我真的不知道该怎么做。

我正在使用 Ruby 1.9.3、Rails 3.2.6 和 rspec-rails 2.11.0。谢谢。

4

5 回答 5

13

通过结合 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
于 2013-05-09T17:23:51.523 回答
7

目前您无法在路由规范中发送额外的标头,这是因为line 608actionpack-3.2.5/lib/action_dispatch/routing/route_set.rb说:

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

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

{
 "rack.version"=>[1, 1],
 "rack.input"=>#<StringIO:0xb908f5c>,
 "rack.errors"=>#<StringIO:0xb908fac>,
 "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 匹配器错误之外,您应该在指定方法和路径的 Hash 上调用它,如下所示:

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

让我们知道您是否能够模拟该 env_for 并让它返回您的标头,很高兴知道。

问候克里斯托夫

于 2012-07-19T11:48:12.163 回答
5
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
于 2012-10-15T12:09:50.577 回答
5

您可以使用 rspecand_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
于 2016-08-01T15:33:04.913 回答
0

对于 Rails 3 和 4,我在RSpec 中围绕 hook完成了以下操作:

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

这有点骇人听闻,但它就像一个魅力。

于 2017-12-30T18:03:32.643 回答