在RSpec 3请求中设置标头

lig*_*h05 23 rspec ruby-on-rails ruby-on-rails-4.1 rspec3

我正在尝试为需要身份验证的某些RSpec请求设置标头.标题是ACCESS_TOKEN.无论我如何设置标头,它都永远不会被设置.我知道应用程序有效,因为我可以手动测试它,我只是不能让rspec测试工作.请在此处查看此问题的完整源代码和测试:https://github.com/lightswitch05/rspec-set-header-example

由于在我的大多数请求规范中都使用了身份验证,因此我创建了支持帮助程序模块来检索访问令牌并将其设置在标头中.下面是我如何设置标题的摘要,查看我在完整源代码中尝试过的所有内容

# my_app/spec/support/session_helper.rb
module SessionHelper
  def retrieve_access_token
    post api_v1_session_path({email: 'test@example.com', password: 'poor_password'})

    expect(response.response_code).to eq 201
    expect(response.body).to match(/"access_token":".{20}"/)
    parsed = JSON(response.body)
    token = parsed['access_token']['access_token']

    @request.headers['HTTP_ACCESS_TOKEN'] = token
  end
end
Run Code Online (Sandbox Code Playgroud)

一个使用此帮助程序的示例请求规范应该可以工作,但总是失败,因为标头永远不会被设置:

# my_app/spec/requests/posts_spec.rb
# ...
context "create" do
  it "creates a post" do
    retrieve_access_token
    post = FactoryGirl.build(:post)

    post api_v1_posts_path(
      post: {
        title: post.title,
        content: post.content
      }
    )

    expect(response.body).to include('"id":')
    expect(response.body).to include('"title":"' + post.title + '"')
    expect(response.body).to include('"content":"' + post.content + '"')
    expect(response.response_code).to eq 201
  end
end
Run Code Online (Sandbox Code Playgroud)

我知道我可以在个人getpost请求中手动设置标头- 但这不是API范围授权的可维护解决方案.想象一下,如果标题名称略有改变,则必须更改每个测试.

Sur*_*rya 40

注意:这个答案是基于你似乎在呼唤什么api_v1_session_pathpost要求SessionsController每一个规范你想在您的要求规范运行.

有两种方法可以解决我在这里遇到的问题.

解决方案#1 - 您可以在您SessionHelper或其他一些名为support/requests_helper.rb的帮助程序文件中创建另一个帮助程序方法(但您更喜欢).我在support/requests_helper.rb中创建了另一个帮助器:

module RequestsHelper
  def get_with_token(path, params={}, headers={})
    headers.merge!('HTTP_ACCESS_TOKEN' => retrieve_access_token)
    get path, params, headers
  end

  def post_with_token(path, params={}, headers={})
    headers.merge!('HTTP_ACCESS_TOKEN' => retrieve_access_token)
    post path, params, headers
  end

  # similarly for xhr..
end
Run Code Online (Sandbox Code Playgroud)

然后在rails_helper.rb中:

  # Include the sessions helper
  config.include SessionHelper, type: :request
  # Include the requests helper
  config.include RequestsHelper, type: :request
Run Code Online (Sandbox Code Playgroud)

更改session_helper.rb:

# my_app/spec/support/session_helper.rb
module SessionHelper
  def retrieve_access_token
    post api_v1_session_path({email: 'test@example.com', password: 'poor_password'})

    expect(response.response_code).to eq 201
    expect(response.body).to match(/"access_token":".{20}"/)
    parsed = JSON(response.body)
    parsed['access_token']['access_token'] # return token here!!
  end
end
Run Code Online (Sandbox Code Playgroud)

现在,您可以更改所有请求规范,如下所示:

describe Api::V1::PostsController do

  context "index" do
    it "retrieves the posts" do
      get_with_token api_v1_posts_path

      expect(response.body).to include('"posts":[]')
      expect(response.response_code).to eq 200
    end

    it "requires a valid session key" do
      get api_v1_posts_path

      expect(response.body).to include('"error":"unauthenticated"')
      expect(response.response_code).to eq 401
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

解决方案#2 - 将specs/factories/access_token_factory.rb更改为:

FactoryGirl.define do
  factory :access_token do
    active true
  end

  # can be used when you want to test against expired access tokens:
  factory :inactive_access_token do
    active false
  end
end
Run Code Online (Sandbox Code Playgroud)

现在,更改您要使用的所有请求规范access_token:

describe Api::V1::PostsController do

  context "index" do
    let(:access_token){ FactoryGirl.create(:access_token) }

    it "retrieves the posts" do
      # You will have to send HEADERS while making request like this:
      get api_v1_posts_path, nil, { 'HTTP_ACCESS_TOKEN' => access_token.access_token }

      expect(response.body).to include('"posts":[]')
      expect(response.response_code).to eq 200
    end

    it "requires a valid session key" do
      get api_v1_posts_path

      expect(response.body).to include('"error":"unauthenticated"')
      expect(response.response_code).to eq 401
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

我会选择" 解决方案#1 ",因为它会消除HTTP_ACCESS_TOKEN每次您想要发送此类请求时记住发送标头的负担.

  • 对于只想使用RSpec 3和Rails 4添加标题的人,只需在`get`,`post`等之前的任何地方调用`request.headers.merge!({'X-MYHEADER':'value'})` .将标头添加到控制器规范的请求中.请参阅此要点https://gist.github.com/quadrolls/9203f924f934398f6992162bbbcb1a0c (4认同)

Luc*_*cas 14

常见的误解是平等对待控制器和请求测试.

从阅读有关控制器规格请求规格开始,这将是一件好事.如您所见,控制器规范模拟http请求,而请求规范执行完整堆栈请求.

您可以找到一些关于为什么要编写控制器规范以及在此处测试的内容的好文章.虽然写它们很好,但在我看来它们不应该触及数据库.

因此,虽然Voxdei答案部分有效(在将请求规格更改为控制器规格之后,您的设置标头的方式将起作用),但我忽略了这一点.

在请求规范中,您不能只使用请求/控制器方法,您必须将哈希中的标头作为请求方法的第三个参数传递,因此即

post '/something', {}, {'MY-HEADER' => 'value'}
Run Code Online (Sandbox Code Playgroud)

你可以做的是对存根认证,如:

before do
  allow(AccessToken).to receive("authenticate").and_return(true)
end
Run Code Online (Sandbox Code Playgroud)

然后,您可以在一个规范中测试您的身份验证,以确保它可以正常工作,并在其他规范中使用之前进行过滤.这也可能是更好的方法,因为每次运行需要身份验证的规范时执行额外的请求都是非常巨大的开销.

我还在grape gem中发现了非常有趣的pull请求,它试图添加默认的标题行为,所以如果你真的想在请求规范中使用默认标题,你也可以尝试这种方法.