如何期望加薪ActiveRecord::RecordNotFoundrspec?

How to expect raise ActiveRecord::RecordNotFound rspec?

这个错误如何通过测试?

Rspec 控制器和结果

context 'invalid confirmation_token' do
      subject do
        post signup_step5_path,
             params: {
               user: {
                 password: 'hoge',
                 password_confirmation: 'hoge',
                 confirmation_token: 'wrong_token'
               }
             }
      end

      let(:user) { User.find_by(confirmation_token: 'testtesttest') }

      it 'does not update user attributes and never create an end_point record' do
        expect { subject }.raise_error(ActiveRecord::RecordNotFound)

expected ActiveRecord::RecordNotFound but nothing was raised

控制器方法 我拯救ActiveRecord::RecordNotFound并在私有方法中渲染404页面。


class Users::SignupController < ApplicationController
  layout 'devise'

  rescue_from ActiveRecord::RecordNotFound, with: :render404


def step5
    @user = User.find_by(confirmation_token: step5_params[:confirmation_token])
    raise ActiveRecord::RecordNotFound unless @user
    .....
    end

private

def render404(error = nil)
    logger.info "Rendering 404 with exception: #{error.message}" if error
    render file: Rails.root.join('public/404.ja.html'), status: :not_found
  end

end

假设它是 request spec,请求将 return HTTP 404,您可以为此设置期望值:

is_expected.to be_not_found

旁注:

@user = User.find_by(confirmation_token: step5_params[:confirmation_token])
raise ActiveRecord::RecordNotFound unless @user

可以简化为:

@user = User.find_by!(confirmation_token: step5_params[:confirmation_token])

首先解释异常匹配器实际上只会匹配未捕获的异常可能是个好主意。那是因为它基本上只是一个救援语句,并在调用堆栈冒泡时拯救异常,它旨在测试一段代码是否引发了一个由消费者捕获的异常——这是测试 行为.

另一方面,测试代码引发和挽救异常是测试它如何工作。

def foo
  raise SomeKindOfError
end

def bar
  begin 
    raise SomeKindOfError
  rescue SomeKindOfError
    puts "RSpec will never catch me!"
  end
end

describe "#foo" do
  it "raises an exception" do
    expect { foo }.to raise_exception(SomeKindOfError)
  end
end

describe "#bar" do
  it "rescues the exception" do
    expect { bar }.to_not raise_exception(SomeKindOfError)
  end
end

当您使用 rescue_from 时,它基本上只是使用 around_action 回调来挽救给定异常的语法糖:

class ApplicationController
  around_action :handle_errors

  private

  def handle_errors
    begin 
      yield
    rescue SomeKindOfError
      do_something
    end
  end
end

虽然 RSpec 曾经有过 bypass_rescue 控制器规格,但 Rails 和 RSpec 团队和您都非常不鼓励使用控制器规格我们实际上只是在测试实现而不是行为。

相反,您应该测试实际控制器做了什么,而不是它是如何做的。

context 'invalid confirmation_token' do
  # explicit use of subject is a code smell
  before do
    post signup_step5_path,
      params: {
        user: {
          password: 'hoge',
          password_confirmation: 'hoge',
          confirmation_token: 'wrong_token'
        }
      }
  end
  let(:user) { User.find_by(confirmation_token: 'testtesttest') }
  
  it 'does not update the users password' do
    expect(user.valid_password?('hoge')).to be_falsy
  end
  
  it 'returns a 404 - NOT FOUND' do
    expect(response).to have_http_status(:not_found)
  end
  
  # using Capybara in a feature spec is a better way to do this.
  it 'renders something' do
    expect(response.body).to match("Oh Noes!") 
  end
end