This team of 2 people wants to…

try behavior-driven development

See everyone with this goal (5 people)

People doing this as a team:


Entries from people on this team:

try behavior-driven development
Ruby, why donchu drop dem pants... 2 years ago

RSpec’sAPI for verifying collection behavior (not testing!) reads so well I just had to share with a fellow professional .NET coder (albeit a Ruby enthusiast also). Low-brow hilarity ensued…

zerokarmaleft: damn, i just love reading code that looks like this:
specify “all Players’ hands should have 2 cards” do
  @game.players.each { |player| player.hand.should_have( 2 ).cards }
end

zerokarmaleft: instead of:
[Test]
public void TestAllHandsAfterDealing() {
  foreach(Player player in game.Players) {
    Assert.AreEqual( 2, player.Hand.Cards );
  }
}

jm: yeah thats pretty nice code

jm: i’d stick my dick in that code

zerokarmaleft: rofl

jm: its puuuurdy

If you haven’t figured it out already, I started writing a generic card game (buzzword warning) framework to get my feet wet with BDD. Even after my limited foray, I feel that RSpec encourages thinking from a high-level abstraction standpoint and finding descriptive names. Both very good things in my book. To get this particular specification to read naturally, I had to extract an Array attribute out of a class and wrap it in a new class.

class Player
  attr_reader :hand
  def initialize; @hand = Array.new; end
end
became
class Player
  attr_reader :hand
  def initialize; @hand = Hand.new; end
end
class Hand
  attr_reader :cards
  def initialize; @cards = Array.new; end
end

In this minimal form, Hand is fairly worthless, but I wasn’t sure what kind of functionality it needed. So I added some more Player specs to see what would surface. In a card game like Uno, there are many times you have several cards that are the same and playing one is equivalent to playing any other. But Players shouldn’t be concerned with selecting a specific Card instance and deleting it from his/her Hand. They should just be able to say, “I want to play a Skip card, and I don’t care which one.”

class Player
[snip]
  def play( klass_of_card, discard_pile )
    cards = @hand.cards.select { |c| c.is_a? klass_of_card }
    raise CardNotInHandError if cards.empty?
    discard_pile << @hand.cards.delete( cards.first )
  end
end
class Card; end
class Skip < Card; end
@edward = Player.new
@edward.play Skip

Since I want to pass a Card’s class as a parameter instead of an instance I can’t use Array#include? to cleanly test for inclusion. I wanted a similar one-liner with Hand.

class Player
[snip]
  def play( card, discard_pile )
    raise CardNotInHandError unless @hand.has_this? card
    discard_pile << @hand.discard( card )
  end
end
class Hand
[snip]
  def has_this?( klass_of_card )
    @cards.select { |card| card.is_a? klass_of_card }
    @cards.empty?
  end
  def discard( klass_of_card )
    @cards.delete { |card| card.is_a? klass_of_card }
  end
end

Still doesn’t look like much but I think the clarity in Player is increased by isolating class-handling logic in Hand. As far as the Player is concerned, he/she is playing a Card…not a Card.class.

My experience with RSpec has been pretty positive so far. I’ll get back to my humble chess variant project in a bit and see how well test2spec executes Test::Unit migration.