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

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

我刚开始学习 RSpec。我似乎无法理解为什么我的测试 #start method 失败了。

如果有人能向我解释一下,将不胜感激。

我遇到的错误:

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

你嘲笑嘲笑。应该严格应用的规则之一是不要模拟被测单元,所以

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

这样做

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

为什么不模拟被测单元?

      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

因为在示例中,您只是在测试 RSpec 的模拟框架是否有效。简化它

      context '#response method' do
        it 'should say hello' do
          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

看起来更好,模拟的class没有直接测试,而是被测试单元(Card)使用。

如果您正在学习 RSpec - 尽量少模拟。这将迫使您设计 classes 以便于测试。现在您正在以旧的习惯性方式设计它,这使得 class 难以测试,这迫使您使用模拟(并且由于您只是在学习 - 很容易在您没有的地方发现 placec '测试任何东西)