Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't stub things with Rspec

I have a Rails 4 application, and here is my lib/foobar:

jan@rmbp ~/D/r/v/l/foobar> tree
.
├── foo_bar.rb
└── foobar_spec.rb

0 directories, 2 files

And the files:

foobar_spec.rb

require "spec_helper"

describe "FooBar" do
  subject { FooBar.new }
  its(:foo) { should == "foo"}
  #stubbed version of test crashes
  #FooBar.stub(:foo).and_return("bar")
  #subject { FooBar.new }
  #its(:foo) { should == "bar"}

end

foo_bar.rb

class FooBar
  def foo
    "foo"
  end
end

spec_helper.rb:

# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
# commented for zeus two runs bug
require 'rspec/autorun'
require 'capybara/rspec'

# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)

RSpec.configure do |config|

  config.include Devise::TestHelpers, :type => :controller
  config.include Features::SessionHelpers, type: :feature

  # ## Mock Framework
  #
  # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
  #
  # config.mock_with :mocha
  # config.mock_with :flexmock
  # config.mock_with :rr
  config.mock_with :rspec

  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

  # If true, the base class of anonymous controllers will be inferred
  # automatically. This will be the default behavior in future versions of
  # rspec-rails.
  config.infer_base_class_for_anonymous_controllers = false

  # 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"
end

The spec passes fine. But when I uncomment this line:

# FooBar.stub(:foo).and_return("bar")

It fails with:

/Users/jan/.rvm/gems/ruby-2.0.0-p247/gems/rspec-mocks-2.14.3/lib/rspec/mocks.rb:26:in `proxy_for': undefined method `proxy_for' for nil:NilClass (NoMethodError)

What is wrong?

EDIT: Also, I can't seem to be able to use webmock either.

  stub_request(:post, "https://accounts.google.com/o/oauth2/token")
         .with(:body => { "client_id"     => CLIENT_ID,
                         "client_secret" => CLIENT_SECRET,
                         "refresh_token" => refresh_token, }
         ).to_return(:status => 200,
                     :body => File.read("#{$fixtures}/refresh_token.json"))

Returns:

/Users/jan/Documents/ruby/vince.re/lib/youtube/you_tube_test.rb:9:in block in <top (required)>': undefined methodstub_request' for < Class :0x007f8159bbe7c0> (NoMethodError)

SOLUTION: Thanks to @gotva for telling me about stubs requirement to reside within it blocks. Here is my new, fixed webmock test, and it works great:

  context "when token is nil it" do
    it "called refresh method" do
      YouTube.any_instance.should_receive(:refresh_auth_token).with(data["refresh"]) 
      YouTube.new(data["uid"], nil, data["refresh"])
    end
    it "refreshed the authentation token" do
      stub_request(:post, "https://accounts.google.com/o/oauth2/token")
            .with(:body => { "client_id"     => CLIENT_ID,
                        "client_secret" => CLIENT_SECRET,
                        "grant_type"=>"refresh_token",
                        "refresh_token" => data["refresh"], }
            ).to_return(:status => 200,
                        :body => File.read("#{$fixtures}/refresh_token.json"))
        yt = YouTube.new(data["uid"], nil, data["refresh"])
        yt.token.should == data["access_token"]
    end
  end
like image 374
if __name__ is None Avatar asked Sep 08 '13 17:09

if __name__ is None


People also ask

What does stub do in RSpec?

In RSpec, a stub is often called a Method Stub, it's a special type of method that “stands in” for an existing method, or for a method that doesn't even exist yet. In our example, the allow() method provides the method stubs that we need to test the ClassRoom class.

What is the difference between stubs and mocks in ruby testing?

The difference between mocks and stubsA Test Stub is a fake thing you stick in there to trick your program into working properly under test. A Mock Object is a fake thing you stick in there to spy on your program in the cases where you're not able to test something directly.

How do I mock an instance variable in RSpec?

You can't mock an instance variable. You can only mock methods. One option is to define a method inside OneClass that wraps the another_member , and mock that method. However, you don't have to, there is a better way to write and test your code.

What is mock in RSpec?

Generally speaking, a mock is a replica or imitation of something. RSpec mocks, in the same sense, are an imitation of return values or method implementations. The ability to carry out this kind of imitation makes it possible to set expectations that specific messages are received by an object.


1 Answers

It seems to me that using this stub

FooBar.stub(:foo).and_return("bar")

you are trying to stub nonexistent method. Method foo is instance method but you stub class method.

If you would like to stub instance method FooBar#foo use any_instance

FooBar.any_instance.stub(:foo).and_return("bar")

Update from comments

Apply stub in it or before blocks.

new variant:

it 'place here some description for method foo' do
  FooBar.any_instance.stub(:foo).and_return("bar") 
  expect(FooBar.new.foo).to eql('bar')
end 

or

# the order is important! 
describe "FooBar" do 
  before { FooBar.any_instance.stub(:foo).and_return("bar") } 
  subject { FooBar.new } 
  its(:foo) { should == "foo"} 
end
like image 182
gotva Avatar answered Sep 25 '22 08:09

gotva