Tagged: rspec Toggle Comment Threads | Keyboard Shortcuts

  • ThomasPowell 9:37 am on April 6, 2022 Permalink | Reply
    Tags: , rspec,   

    RSpec::Mocks::VerifyingMessageException does not implement method 

    Why am I trying to call RSpec::Mocks::VerifyingMessageException?

    The problem started with an attempt to create a stub like the following:

        new_net=allow(Net::SFTP).to receive(:start).and_return(double('sftp'))
        allow(new_net).to receive(:upload_file!).and_raise('Broken')
    

    Which resulted in the following failure

         Failure/Error: allow(new_net).to receive(:upload_file!).and_raise('Broken')
           #<RSpec::Mocks::VerifyingMessageExpectation Net::SFTP.start(any arguments)> does not implement: upload_file!
    

    It’s a pretty obscure error to look up because RSpec::Mocks::VerifyingMessageException is not a class you would normally explicitly interact with. So what happened?

    RSpec::Mocks::VerifyingMessageException is the class of an allow call

    new_net=allow(Net::SFTP).to receive(:start).and_return(double('sftp')) isn’t saving off the double, but the result of the allow call. While at a fresher point in the day, I would have noticed that, I was somehow expecting the double itself to be returned. However, usually when an object is returned, it’s part of the method chain, not an argument in a part of the chain.

    The proper setup would have been to save the double, set it as a return value, and stub a method on it:

            new_net=double('sftp')
            allow(Net::SFTP).to receive(:start).and_return(new_net)
            allow(new_net).to receive(:upload_file!).and_raise('Broken')
    

    (The above example is a bit contrived for illustration purposes and is also not necessarily the best approach for a given circumstance.)

    A related error when using and_wrap_original

    If you’re wrapping the original new of a class and then want to add stubs, you can use and_wrap_original, but beware a pitfall (again, contrived example):

            allow(String).to receive(:new).and_wrap_original do |m, *args|
              string = m.call(*args)
              allow(string).to receive(:downcase).and_return('downcase')
            end
    

    Which will generate an error like:

         1.2) Failure/Error: String.new('hi').downcase
    
              NoMethodError:
                undefined method `downcase' for #<RSpec::Mocks::VerifyingMessageExpectation:0x00007fede75f9380>
    

    Fortunately, this is a simple case of not having the subject object (that needs to be returns by the method) as the last thing evaluated in the block:

            allow(String).to receive(:new).and_wrap_original do |m, *args|
              string = m.call(*args)
              allow(string).to receive(:downcase).and_return('downcase')
              string
            end
    

    Conclusion

    I hope that you don’t back yourself into these corners with stubbing methods, but if you manage to get this error, I hope you either find this post or have remembered it for the future.

     
  • ThomasPowell 7:34 am on July 13, 2021 Permalink | Reply
    Tags: factory_bot, factory_girl, rspec, , , trait   

    Referencing one trait from another trait in factory_bot 

    Sometimes you want to DRY up traits by referencing one trait from another trait in factory_bot. I tried searching on “inheriting traits” (that’s just for one factory inheriting traits from another and was in a factory_bot issue in GitHub). I accidentally stumbled upon the answer in a slightly unrelated StackOverflow question about calling a trait from another trait with params in factory_girl.

    Ultimately, you use the trait name from the first trait as a method invocation in the referencing trait:

    FactoryBot.define do
      factory :user do
        role
        trait :with_supervisor do
          # complex set up might go
          # here
          after(:create) do |user|
            supervisor { create(:user) }
          end
        end
        trait :with_organization do
          with_supervisor # invoke the other trait first
          organization
        end
      end
    end
    
     
  • ThomasPowell 8:48 pm on July 6, 2021 Permalink | Reply
    Tags: rspec,   

    RAW_POST_DATA in rspec rails for Rails 5.2 and beyond 

    The last time I was trying to specify RAW_POST_DATA in rspec was probably Rails 3 or 4, but I ran into a situation trying to test an edge case for error handling where I wanted that same functionality. I quickly found this issue [Unable to POST raw request body], but didn’t immediately figure out what wasn’t being set correctly.

    In this case the test setup I was using was setting multipart/form-data instead of application/xml on the content types:

    {:HTTP_ACCEPT=>"application/xml", :HTTP_CONTENT_TYPE=>"multipart/form-data", :CONTENT_TYPE=>"multipart/form-data"}
    

    Because of this, the Rails controller tests that rspec hooks into was trying to break following malformed xml down to parameters:

                <test>
                  <data&nbsp;
                  <![CDATA[THIS|IS|SENSITIVE|BUT|MALFORMED]]>
                  </data>
                </test>
    
     Minitest::Assertion:
       Expected response to be a <400: bad_request>, but was a <422: Unprocessable Entity>
       Response body: <errors>
           <error>["<test>\n  <data", "nbsp;\n  <!"] are not permitted parameters</error>
       </errors>
    

    I finally noticed that the mime-type might be involved. In this code, Content-Type was also an issue, so:

    • Removed HTTP_CONTENT_TYPE from the headers
    • Set CONTENT_TYPE header to 'application/xml' instead of 'multipart/form-data' to prevent automatic params parsing in this case.
    • Passed as: :xml into the test to get the 'mime-type' correct.

    Ultimately, if your code hasn’t boxed you in, then the as: :xml and passing raw data as a parameter should work:

    post things_path, params: raw_xml_data, headers: non_form_data_headers, as: :xml
    
    ## replacement for the following:
    # @request.env['RAW_POST_DATA'] = raw_xml_data
    # post things_path
    
     
c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
Reply
e
Edit
o
Show/Hide comments
t
Go to top
l
Go to login
h
Show/Hide help
shift + esc
Cancel
%d bloggers like this: