Kris MP
Kris MP

Reputation: 2415

RSpec how to test an argument data type passed to a method

I need to test the passed argument type is an integer. Here is my test spec:

require 'ball_spin'

RSpec.describe BallSpin do
  describe '#create_ball_spin' do
    subject(:ball_spin) { BallSpin.new }
    it 'should accept an integer argument' do
      expect(ball_spin).to receive(:create_ball_spin).with(an_instance_of(Integer))
      ball_spin.create_ball_spin(5)
    end
  end
end

My code:

class BallSpin
  def create_ball_spin n
    "Created a ball spin #{n} times" if n.is_a? Integer
  end
end

Thanks in advance

UPDATE:

Apologize for using old RSpec syntax, below I updated my code to use the latest one:

it 'should accept an integer argument' do
  expect(ball_spin).to receive(:create_ball_spin).with(an_instance_of(Integer))
  ball_spin.create_ball_spin(5)
end

Upvotes: 4

Views: 7731

Answers (3)

Antonio Ganci
Antonio Ganci

Reputation: 515

I think the reason is that 5 is an instance of Fixnum and not Integer:

2.2.1 :005 > 5.instance_of?(Fixnum)
  => true 
2.2.1 :006 > 5.instance_of?(Integer)
  => false 

UPDATE: Ok, I've tried your code and the problem is Integer instead of Fixnum. Here is the correct assertion:

RSpec.describe BallSpin do
  describe '#create_ball_spin' do
    subject(:ball_spin) { BallSpin.new }
    it 'should accept an integer argument' do
      expect(ball_spin).to receive(:create_ball_spin).with(an_instance_of(Fixnum))
      ball_spin.create_ball_spin(5)
    end
  end
end

Upvotes: 2

spickermann
spickermann

Reputation: 106972

The use case of the receive matcher is to spec that a method is called by someone. But it is worth noting that the matcher itself doesn't call that method nor it tests if the method exists or if the list of possible arguments matches a given pattern.

It seems like your code doesn't call that method at all. A simple test that should pass might look like this:

subject(:ball_spin) { BallSpin.new }

it 'is called with an integer argument' do
  ball_spin.should_receive(:create_ball_spin).with(an_instance_of(Integer))
  ball_spin.create_ball_spin(5) # method called
end

it 'is not called' do
  ball_spin.should_not_receive(:create_ball_spin)
  # method not called
end

See section Argument Matcher.

Btw you use the old RSpec syntax and might want to consider updating your test suite to the new expect syntax.

Upvotes: 1

Ilya Lavrov
Ilya Lavrov

Reputation: 2860

You may add a block to receive to check the method params:

expect(ball_spin).to receive(:create_ball_spin) do |arg|
  expect(arg.size).to be_a Integer
end

You may find details in Arbitrary Handling section of rspec-mocks documentation.

UPDATE: Also you may use the same approach with should syntax:

ball_spin.should_receive(:create_ball_spin) do |arg|
  arg.should be_a Integer
end

Upvotes: 11

Related Questions