pie109
pie109

Reputation: 115

rspec stub to allow [hash_key] to be passed

How do you create a rspec method stub to allow a response from a method that takes in the hash key to return its value?

This is the line I want to test

sub_total = menu.menu_items[item] * quantity

and I'm using this line in rspec as my test stub on a double.

allow(menu).to receive(:menu_items[item]).and_return(2.0)

My env is set up with ruby 2.2.0 and spec 3.1.7

However I keep on getting a

NameError: undefined local variable or method `item'

Ruby code

def place_order(item, quantity, menu)
   sub_total = menu.menu_items[item] * quantity
   @customer_order << [item, quantity, sub_total]
 end

Rspec code

 let(:menu) { double :menu }

   it "should allow 1 order of beer to placed" do
     order = Order.new
     allow(menu).to receive(:menu_items[item]).and_return(2.0)
     order.place_order(:Beer, 1, 2.0)
     expect(order.customer_order).to eq [[:Beer, 1, 2.0]]
  end

Failures:

  1) Order should allow 1 order of beer to placed
     Failure/Error: allow(menu).to receive(:menu_items[item]).and_return(2.0)
     NameError:
       undefined local variable or method `item' for #<RSpec::ExampleGroups::Order:0x007fbb62917ee8 @__memoized=nil>
     # ./spec/order_spec.rb:9:in `block (2 levels) in <top (required)>'

I've tried a number of things but nothing has worked

allow(menu).to receive(:menu_items).and_return(2.0)
allow(menu).to receive(:menu_items).with(item).and_return(2.0)
allow(menu).to receive(:menu_items).with("item").and_return(2.0)
allow(menu).to receive(:menu_items).with([item]).and_return(2.0)

I've run my code in irb and I can see it works but I can't find a way to get my class double to recerive the hash key.

Upvotes: 11

Views: 16177

Answers (4)

demonodojo
demonodojo

Reputation: 422

you can do this:

allow(menu.menu_items).to receive(:[]).and_return({Beer: 2.0})

You can also pass an specific item if you need:

allow(menu.menu_items).to receive(:[]).with(1).and_return({Beer: 2.0})

Upvotes: 11

Jon Kern
Jon Kern

Reputation: 3235

Thanks to @SimoneCarletti's answer, I was able to easily stub an instance of PublicActivity. I add this answer only as a more brief (re)statement of the OP's problem and the simplicity of the solution.

Code I want to mimic with a stub:

self.entity = activity.parameters['entity_string']

And the salient parts of the test double:

activity = double('activity') # PublicActivity
allow(activity).to receive(:parameters).and_return({'entity_string' => "some entity name"})

Full code:

class ActivityRenderer

  attr_accessor :time
  attr_accessor :user
  attr_accessor :action
  attr_accessor :entity

  def initialize(activity)
    self.entity = activity.parameters['entity_string']
    self.time = activity.updated_at
    self.user = User.find(activity.owner_id)
    self.action = activity.key
  end
end

RSpec.describe ActivityRenderer do
  let(:user) { ...factory girl stuff... }
  let(:now) { Time.zone.now }

  before do
    Timecop.freeze
  end

  it 'provides an activity renderer' do
    activity = double('activity') # PublicActivity
    allow(activity).to receive(:parameters).and_return({'entity_string' => "some entity name"})
    allow(activity).to receive(:updated_at).and_return(now)
    allow(activity).to receive(:owner_id).and_return(user._id)
    allow(activity).to receive(:key).and_return('some activity?')

    ar = ActivityRenderer.new(activity)

    expect(ar.user).to eql(user)
    expect(ar.time).to eql(now)
    expect(ar.action).to eql('some activity?')
    expect(ar.entity).to eql("some entity name")
  end
end

Upvotes: 0

Simone Carletti
Simone Carletti

Reputation: 176412

The line menu.menu_items[item] is in reality composed by 3 method calls. [] is a call to the method [] on the Hash returned by menu_items.

I assume menu.menu_items returns a Hash and not an Array, given in the spec item is a Symbol.

That means your stub requires a little bit more work.

allow(menu).to receive(:menu_items).and_return({ Beer: 2.0 })

Also note, the error

undefined local variable or method `item'

is because you were using item in the spec, but item is not defined outside your method.

Upvotes: 8

Mohammad AbuShady
Mohammad AbuShady

Reputation: 42849

you're going a little too deep with your stub, think of this instead

allow(menu).to receive(:menu_items).and_return({Beer: 2.0})

Upvotes: 0

Related Questions