【问题标题】:How to expect raise ActiveRecord::RecordNotFound rspec?如何期望提高 ActiveRecord::RecordNotFound rspec?
【发布时间】:2022-01-10 16:04:05
【问题描述】:

如何获得这个错误的测试通过?

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

【问题讨论】:

    标签: ruby-on-rails activerecord error-handling rspec


    【解决方案1】:

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

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

    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
    

    【讨论】:

      【解决方案2】:

      假设它是request spec,则请求将返回 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])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多