Reputation: 1511
I'm using RSpec for the first time to create a Poker game with Card, Deck, Hand, Player, & Game classes. I've completed the Card, Deck, & Hand tests/classes, and I'm currently writing the Player test/class. Its gotten more difficult to rely on mocks/stubs as the dependencies have grown and I am running into an issue where my Player#discard method testing is throwing an error because RSpec is throwing this error:
1) Player#discard discards the chosen card
Failure/Error: player.discard
Double "hand" received unexpected message :delete_if with (no args)
# ./lib/player.rb:19:in `block in discard'
# ./lib/player.rb:18:in `each'
# ./lib/player.rb:18:in `discard'
# ./spec/player_spec.rb:34:in `block (3 levels) in <top (required)>'
I'm new to RSpec & not really sure what I'm doing wrong. Any help & advice on ways to avoid using the actual class objects in my specs to make them pass would be greatly appreciated. Here is my player_spec & Player class so far.
./spec/player_spec.rb
require 'rspec'
require 'player'
describe Player do
subject(:player) { Player.new }
its(:hand) { double("hand", :cards => []) }
its(:pot) { should eq 1000 }
let(:ace_spades) { double("card", :suit => :spades, :value => :ace) }
let(:ace_clubs) { double("card", :suit => :clubs, :value => :ace) }
let(:king_spades) { double("card", :suit => :spades, :value => :king) }
let(:king_clubs) { double("card", :suit => :clubs, :value => :king) }
let(:queen_spades) { double("card", :suit => :spades, :value => :queen) }
let(:queen_clubs) { double("card", :suit => :clubs, :value => :queen) }
let(:jack_spades) { double("card", :suit => :spades, :value => :jack) }
let(:jack_clubs) { double("card", :suit => :clubs, :value => :jack) }
let(:ten_spades) { double("card", :suit => :spades, :value => :ten) }
let(:ten_clubs) { double("card", :suit => :clubs, :value => :ten) }
let(:nine_spades) { double("card", :suit => :spades, :value => :nine) }
let(:nine_hearts) { double("card", :suit => :hearts, :value => :nine) }
let(:nine_clubs) { double("card", :suit => :clubs, :value => :nine) }
let(:nine_diamonds) { double("card", :suit => :diamonds, :value => :nine) }
let(:discard_hand) do
double("hand", :cards => [nine_clubs, ace_spades, nine_hearts, ten_clubs, ten_spades])
end
let(:discard_input) { ["A","S"] }
describe "#discard" do
it "discards the chosen card" do
player.hand.stub(:cards).and_return(discard_hand)
player.stub(:get_input).and_return(discard_input)
player.discard
expect(player.hand.cards).to eq([nine_clubs, nine_hearts, ten_clubs, ten_spades])
end
end
end
./lib/player.rb
class Player
INPUT_VALUES = { "2" => :deuce, "3" => :three, "4" => :four,
"5" => :five, "6" => :six, "7" => :seven,
"8" => :eight, "9" => :nine, "10" => :ten,
"J" => :jack, "Q" => :queen, "K" => :king, "A" => :ace }
INPUT_SUITS = { "S" => :spades, "H" => :hearts, "C" => :clubs, "D" => :diamonds }
attr_accessor :pot, :hand
def initialize
@pot = 1000
@hand = Hand.new
end
def discard
discards = get_input("Which card(s) would you like to discard?:")
discards.each do |discard|
self.hand.cards.delete_if do |card|
card.value == discard[0] && card.suit == discard[1]
end
end
end
private
def get_input(prompt)
puts prompt
input = gets.chomp
parse_input(input)
end
def parse_input(input)
value = input.scan(/^(?:[2-9]|10|[jqkaJQKA])/)
suit = input.scan(/[shdcSHDC]$/)
raise "Invalid Input" if values.empty? || suit.empty?
(value[0].match(/[jqkaJQKA]/)) ? value[0].upcase! : value.map!(&:to_i)
suit[0].upcase!
value = INPUT_VALUES[value[0]]
suit = INPUT_SUITS[suit[0]]
[value, suit]
end
end
Upvotes: 0
Views: 263
Reputation: 29419
You're getting the error you're getting because the code you're calling (i.e. discard
) is invoking delete_if
on one of your doubles for which you haven't set an expectation that would allow that. You should check out stub_chain
as a means of achieving this.
More generally, though, I think you're getting into trouble because you're asking the Player
discard method to implement the delete functionality by operating on the cards array directly rather than delegating discard
to Hand
. If you can simplify your code, it will simplify your tests.
Upvotes: 1