简体   繁体   中英

RSpec - how to fix - ArgumentError: wrong number of arguments (given 0, expected 1) - Ruby

I'm new to learning RSpec. I can't seem to understand why my test #start method is failing.

would be grateful if one could explain to me.

the error i am getting:

CardGame
  attributes
    should have a name
  #response method
    should say hello
  #start method
    can only implement class methods that are defined on a class (FAILED - 1)

Failures:

  1) CardGame#start method can only implement class methods that are defined on a class
     Failure/Error:
       def initialize(name)
         @name = name
       end
     
     ArgumentError:
       wrong number of arguments (given 0, expected 1)
     # ./lib/CardGame.rb:4:in `initialize'
     # ./spec/class_double_spec.rb:29:in `block (3 levels) in <top (required)>'

Finished in 0.01178 seconds (files took 0.30252 seconds to load)
3 examples, 1 failure

Failed examples:

rspec ./spec/class_double_spec.rb:27 # CardGame#start method can only implement class methods that are defined on a class

➜  rspec-course 

class_double_spec.rb

[ruby/spec/class_double_spec.rb]

    require 'spec_helper'
    require 'pry'
    require './lib/CardGame'
    require './lib/Deck'
    
    describe CardGame do
      let(:card) { instance_double(CardGame, 
                                  name: 'poker',
                                  response: 'hello')} 
      let(:deck_klass) { class_double(Deck, build: ['Ace', 'Queen']).as_stubbed_const }
    
      context 'attributes' do
        it 'should have a name' do
          expect(card.name).to eq('poker')
        end
      end
    
      context '#response method' do
        it 'should say hello' do
          allow(card).to receive(:response).and_return('hello')
          expect(card.response).to eq('hello')
        end
      end
    
      context '#start method' do
        it 'can only implement class methods that are defined on a class' do
          expect(deck_klass).to receive(:build)
          card.start
          expect(card.cards).to eq(['Ace', 'Queen'])
        end
      end
    end 

CardGame.rb

[ruby/lib/CardGame.rb]

    class CardGame
      attr_accessor :name, :cards
    
      def initialize(name)
        @name = name
      end
    
      def response
        'hello'
      end
    
      def start
        @cards = Deck.build
      end
    end

Deck.rb

[ruby/lib/Deck.rb]

    class Deck
      def self.build
        # business logic to build cards
      end
    end

You mock to mock. One of the rules one should strictly apply is not to mock unit under test, so instead of

describe CardGame do
      let(:card) { instance_double(CardGame, 
                                  name: 'poker',
                                  response: 'hello')} 
      let(:deck_klass) { class_double(Deck, build: ['Ace', 'Queen']).as_stubbed_const }

      context 'attributes' do
        it 'should have a name' do
          expect(card.name).to eq('poker')
        end
      end
end

do this

describe CardGame do
      let(:card) { CardGame.new(name) }
      let(:name) { 'poker'}

      context 'attributes' do
        it 'should have a name' do
          expect(card.name).to eq('poker')
        end
      end
end

Why should you not mock the unit under test?

      context '#response method' do
        it 'should say hello' do
          allow(card).to receive(:response).and_return('hello')
          expect(card.response).to eq('hello')
        end
      end

Because in the example you're only testing that the RSpec's mocking framework works. Simplify it

      context '#response method' do
        it 'should say hello' do
          expect(card.response).to eq('hello')
        end
      end

The last example:

      context '#start method' do
        it 'can only implement class methods that are defined on a class' do
          expect(deck_klass).to receive(:build)
          card.start
          expect(card.cards).to eq(['Ace', 'Queen'])
        end
      end

Looks better, the mocked class is not directly tested, but used by the unit under test ( Card ).

If you're learning RSpec - try to mock as little as possible. This will force you to design your classes so they're easy to test. Right now you're designing it in your old habitual way, that makes the class hard to test, which forces you to use mocks (and since you're just learning - it's easy to not spot the placec where you don't test anything)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM