如何在 RSpec 中使用 HTTP 状态码符号?

32

在控制器中,我会使用HTTP状态码符号,例如:

render json: {
    auth_token: user.authentication_token, 
    user: user
  }, 
  status: :created
或者
render json: {
    errors: ["Missing parameter."]
  }, 
  success: false, 
  status: :unprocessable_entity

在我的请求规范代码中,我希望也能使用这些符号:

post user_session_path, email: @user.email, password: @user.password
expect(last_response.status).to eq(201)

...

expect(last_response.status).to eq(422)

然而,每次我使用符号而不是整数的测试都失败了:

Failure/Error: expect(last_response.status).to eq(:created)

  expected: :created
       got: 201

  (compared using ==)
这是最新的 Rack HTTP 状态码符号列表。【链接】
4个回答

31

response 对象响应多个符号类型作为消息。因此,您可以简单地执行以下操作:

expect(response).to be_success
expect(response).to be_error
expect(response).to be_missing
expect(response).to be_redirect

对于其他类型,例如:created,您可以创建一个简单的自定义匹配器来包装assert_response
RSpec::Matchers.define :have_status do |type, message = nil|
  match do |_response|
    assert_response type, message
  end
end

expect(response).to have_status(:created)
expect(response).to have_status(404)

这对于已经设置了适当状态的控制器规范来说应该能正常工作。但是,对于功能规范来说,它将无法正常工作。我没有尝试过请求规范,所以在那里您可能会有不同的体验。

之所以可以这样做是因为它利用了RSpec控制器规范背后的类似状态设置。因此,当assert_response访问@response时,它是可用的。

这个匹配器可能可以通过简单地复制assert_response使用的代码来改进:

RSpec::Matchers.define :have_status do |type, message = nil|
  match do |response|
    if Symbol === type
      if [:success, :missing, :redirect, :error].include?(type)
        response.send("#{type}?")
      else
        code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
        response.response_code == code
      end
    else
      response.response_code == type
    end
  end

  failure_message do |response|
    message or
      "Expected response to be a <#{type}>, but was <#{response.response_code}>"
  end
end

更新: 2014-07-02

现在,使用 RSpec Rails 3 可以直接实现此功能:https://www.relishapp.com/rspec/rspec-rails/v/3-0/docs/matchers/have-http-status-matcher


21
这对我有效:
expect(response.response_code).to eq(Rack::Utils::SYMBOL_TO_STATUS_CODE[:not_found])

14

使用 rspec-rails(从rspec 3开始)可以实现

expect(response).to have_http_status(:created)

更新于2018-06-11:

从Rails 6开始,一些匹配器将被替换(例如success将会被替换为successful)。


只有在RSpec 3或更高版本中,have_http_status才包含在其中。RSpec 2中不包括此功能。 - Tinynumbers

12

一方面,响应是通过以下方法构建的:

  • success?(成功?)

  • redirect?(重定向?)

  • unprocessable?(无法处理?)

  • 完整列表请使用:response.methods.grep(/\?/)

另一方面,Rspec断言(predicate)将每个foo? 方法转换为相应的be_foo匹配器(matcher)。

不确定能否以这种方式获得201响应,但创建自定义匹配器(matcher)非常容易。

注意Rails测试仅依赖于少数状态值


1
expect(last_response).to be_success - apneadiving
我承认我没有好好阅读你的评论,但这次出现了错误:“undefined method `success?' for #Rack::MockResponse:0x000000058c1180”。 - JJD
1
请求规范?请求规范应该放在spec/features中,控制器规范应该放在spec/controllers中,否则会添加错误的模块(除非您手动包含它们)。 - apneadiving
我可能误解了,但是 rspec-rails 表示请求规范应该放在 spec/requestsspec/api 或者 spec/integration 目录下。 - JJD
我正在尝试遵循您的建议和像这样的教程,但我仍然无法使其运行。为了避免进一步偏离我的原始问题,这里有一个新帖子 - JJD
显示剩余13条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接