Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruby Class.new returning nil in rspec

We are trying to test that our controller is calling a class a given number of times based on a param we have, but our test is failing in rspec, saying that the initialize method on our class is returning a nil.

We have this controller:

class ProcessFaxesController < ApplicationController
  def create
    email = params["From"]
    attachment_count = params["attachment-count"].to_i
    head :ok

    if attachment_count > 0
      attachment_count.times do |document_index|
        FaxedDocumentProcessor.new(params, document_index+1).perform
      end
    else
      DocumentProcessingMailer.delay.failure_notification(email, "No attachment found.")
    end
  end
end

The external class:

class FaxedDocumentProcessor
  def initialize(params, document_index)
    @params = params
    @attachment = @params["attachment-#{document_index}"]
    @email = @params['From']
  end

  def perform
    # some stuff here
  end
end

With this spec:

require 'rails_helper'

RSpec.describe ProcessFaxesController, type: :controller do
  context "#create" do
    context 'with attachments' do
      before { allow_any_instance_of(FaxedDocumentProcessor).to receive(:perform).and_return(true) }

      it 'calls the FaxedDocumentProcessor attachment-count times' do
        expect(FaxedDocumentProcessor).to receive(:new).twice
        post :create, 'email' => '[email protected]', 'attachment-count' => 2)
      end
    end
  end
end

This is the error we are getting from our test:

Failures:

  1) ProcessFaxesController#create with attachments calls the FaxedDocumentProcessor attachment-count times
     Failure/Error: FaxedDocumentProcessor.new(params, document_index+1).perform

     NoMethodError:
       undefined method `perform' for nil:NilClass
     # ./app/controllers/process_faxes_controller.rb:20:in `block in create'
     # ./app/controllers/process_faxes_controller.rb:19:in `times'
     # ./app/controllers/process_faxes_controller.rb:19:in `create'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/action_controller/metal/implicit_render.rb:4:in `send_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/abstract_controller/base.rb:198:in `process_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/action_controller/metal/rendering.rb:10:in `process_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/abstract_controller/callbacks.rb:20:in `block in process_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:117:in `call'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:555:in `block (2 levels) in compile'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:505:in `call'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:92:in `__run_callbacks__'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:778:in `_run_process_action_callbacks'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:81:in `run_callbacks'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/abstract_controller/callbacks.rb:19:in `process_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/action_controller/metal/rescue.rb:29:in `process_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/action_controller/metal/instrumentation.rb:32:in `block in process_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activesupport-4.2.6/lib/active_support/notifications.rb:164:in `block in instrument'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activesupport-4.2.6/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activesupport-4.2.6/lib/active_support/notifications.rb:164:in `instrument'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/action_controller/metal/instrumentation.rb:30:in `process_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/action_controller/metal/params_wrapper.rb:250:in `process_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/activerecord-4.2.6/lib/active_record/railties/controller_runtime.rb:18:in `process_action'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/abstract_controller/base.rb:137:in `process'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionview-4.2.6/lib/action_view/rendering.rb:30:in `process'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/action_controller/test_case.rb:639:in `process'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/action_controller/test_case.rb:67:in `process'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/devise-3.5.6/lib/devise/test_helpers.rb:19:in `block in process'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/devise-3.5.6/lib/devise/test_helpers.rb:72:in `catch'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/devise-3.5.6/lib/devise/test_helpers.rb:72:in `_catch_warden'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/devise-3.5.6/lib/devise/test_helpers.rb:19:in `process'
     # /Users/mary/.rvm/gems/ruby-2.3.0@website/gems/actionpack-4.2.6/lib/action_controller/test_case.rb:520:in `post'
     # ./spec/controllers/process_faxes_controller_spec.rb:22:in `block (4 levels) in <top (required)>'

We've tried inserting binding.pry into the controller method to poke around ourselves, but whenever we try the call that's blowing up, we don't get any errors.

What could be causing FaxedDocumentProcessor.new to return a nil? And why can't we reproduce the error in pry?

like image 582
oolong Avatar asked Nov 28 '22 14:11

oolong


1 Answers

This is caused by this:

expect(FaxedDocumentProcessor).to receive(:new).twice

The line above completely stubs new method. To make it calling original method, do:

expect(FaxedDocumentProcessor).to receive(:new).twice.and_call_original

Documentation: https://www.relishapp.com/rspec/rspec-mocks/v/2-14/docs/message-expectations/calling-the-original-method

like image 51
BroiSatse Avatar answered Dec 06 '22 00:12

BroiSatse