rspec / rspec-mocks

RSpec's 'test double' framework, with support for stubbing and mocking
https://rspec.info
MIT License
1.16k stars 358 forks source link

Can't convert RSpec::Mocks::Double to String (RSpec::Mocks::Double#to_str gives RSpec::Mocks::Double) #1145

Open amandapouget opened 7 years ago

amandapouget commented 7 years ago

It seems from other issues posted that this was an intentional breaking change in 3.0.0, but from the issues listed I haven't been able to put together a concrete answer to how I should change my code so as not to have this problem. Some suggest putting this in spec_helper:

Rspec::Mocks::Mock.module_eval do
  alias to_str to_s
end

But I cannot get that code to run, and even if I could, would that not just reintroduce bugs with Double#to_str?

Here is my test:

      it "redirects to the customer_agent" do
        allow(CustomerAgent).to receive(:find) { mock_customer_agent(:update_attributes => true) }
        put :update, :id => "1", :customer_id => "24"
        expect(response).to redirect_to(customer_url(mock_customer))
      end

And failing line of code is the first format line:

  def update
    respond_to do |format|
      if @customer_agent.update_attributes(params[:customer_agent])
        format.html { redirect_to(@customer, :notice => 'Customer agent was successfully updated.') }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @customer_agent.errors, :status => :unprocessable_entity }
      end
    end
  end

Thanks in advance for your help!

amandapouget commented 7 years ago

spec_helper file that throws uninitialized constant RSpec::Mocks::Mock (NameError) when I add the three lines at the bottom:

# This file was generated by the `rails generate rspec:install` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause this
# file to always be loaded, without a need to explicitly require it in any files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, make a
# separate helper file that requires this one and then use it only in the specs
# that actually need it.
#
# The `.rspec` file also contains a few flags that are not defaults but that
# users commonly want.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
  # These two settings work together to allow you to limit a spec run
  # to individual examples or groups you care about by tagging them with
  # `:focus` metadata. When nothing is tagged with `:focus`, all examples
  # get run.
  config.filter_run :focus
  config.run_all_when_everything_filtered = true

  # Many RSpec users commonly either run the entire suite or an individual
  # file, and it's useful to allow more verbose output when running an
  # individual spec file.
  if config.files_to_run.one?
    # Use the documentation formatter for detailed output,
    # unless a formatter has already been configured
    # (e.g. via a command-line flag).
    config.default_formatter = 'doc'
  end

  # Print the 10 slowest examples and example groups at the
  # end of the spec run, to help surface which specs are running
  # particularly slow.
  config.profile_examples = 10

  # Run specs in random order to surface order dependencies. If you find an
  # order dependency and want to debug it, you can fix the order by providing
  # the seed, which is printed after each run.
  #     --seed 1234
  config.order = :random

  # Seed global randomization in this process using the `--seed` CLI option.
  # Setting this allows you to use `--seed` to deterministically reproduce
  # test failures related to randomization by passing the same `--seed` value
  # as the one that triggered the failure.
  Kernel.srand config.seed

  # rspec-expectations config goes here. You can use an alternate
  # assertion/expectation library such as wrong or the stdlib/minitest
  # assertions if you prefer.
  config.expect_with :rspec do |expectations|
    # Enable only the newer, non-monkey-patching expect syntax.
    # For more details, see:
    #   - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
    # expectations.syntax = :expect
  end

  # rspec-mocks config goes here. You can use an alternate test double
  # library (such as bogus or mocha) by changing the `mock_with` option here.
  config.mock_with :rspec do |mocks|
    # Enable only the newer, non-monkey-patching expect syntax.
    # For more details, see:
    #   - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
    # mocks.syntax = :expect

    # Prevents you from mocking or stubbing a method that does not exist on
    # a real object. This is generally recommended.
    mocks.verify_partial_doubles = true
  end

  RSpec::Mocks::Mock.module_eval do
    alias to_str to_s
  end
end
JonRowe commented 7 years ago

The solution to your issue is probably that your mock needs to respond to id or to_param in order for Rails to convert it into a url, it's calling to_s as a last resort and wouldn't give the result you want.

The reason why Mock is undefined is that module was an internal api and has since been renamed, (to Double as your error shows), however we don't recommend monkey patching it like this.

xaviershay commented 7 years ago

@JonRowe is that was https://github.com/rspec/rspec-activemodel-mocks is supposed to do? I remember mock_model being a thing a while back but I think we moved it out of core?

JonRowe commented 7 years ago

@xaviershay Yep, that library implements a bunch of "fake active record" functionality.