2

我正在开发二十一点游戏。我的游戏对象包含一个牌组对象,该牌组在牌组达到一定程度的穿透后会被洗牌。我的许多方法都依赖于这个甲板对象。我看不出有任何理由可以通过 setter 方法访问甲板对象。我在测试我的 Game 类上的方法时遇到了麻烦,因为它们取决于牌组的顺序,这是随机的。

例如,我有 deal_hand 方法。

def deal_hand(player)
    reset_deck if @deck.size < 2
    player.hands.push(Hand.new(*@deck.pop(2)))
end

我应该如何测试这样的方法?我在想我可以手动创建一个在 @deck 实例变量中使用的 Deck 对象。不幸的是,我不能设置实例变量,我也不想添加一个设置器,因为除了测试之外没有理由让它成为“可设置的”。我应该从我的测试文件中修补类并添加一个设置器吗?

顺便说一句——我主要编写脚本——我决定在这个项目失控后开始编写测试。是否有“测试模式”的规范资源?

编辑:

我正在使用支持存根/模拟的 MiniTest。尽管据我所知,它只允许您为模拟对象上的方法调用设置预期的返回值。如果我制作了一个 Mock 甲板,实际的甲板对象也取决于一个内部数组。没有调用卡片组的代码直接访问数组。

4

3 回答 3

2

使用模拟库。RSpec 自带一个,但我不喜欢它,所以我将向您展示它的样子Surrogate,我写的那个:

class Deck
  def pop(n)  end
  def reset() end
  def size()  end
end

class Game
  def initialize(deck)
    @deck = deck
  end

  def deal_hand(player)
    reset_deck if @deck.size < 2
    player.hands.push(Hand.new(*@deck.pop(2)))
  end

  def reset_deck
    @deck.reset
  end
end

Hand = Struct.new :card1, :card2

class Player
  def hands
    @hands ||= []
  end
end

require 'surrogate/rspec'
class MockDeck
  Surrogate.endow self
  define(:reset)
  define(:pop)  { |n| n.times.to_a }
  define(:size) { 1 }
end

describe Game, 'deal_hand' do
  let(:deck)   { MockDeck.new }
  let(:player) { Player.new }
  let(:game)   { Game.new deck }

  it 'resets the deck if there are less than 2 cards' do
    deck.will_have_size 2 # set the return value of deck.size
    game.deal_hand player
    deck.was_not told_to :reset # assert what happened to the deck

    deck.will_have_size 1
    game.deal_hand player
    deck.was told_to :reset
  end

  it 'deals the top 2 cards to the player' do
    deck.will_pop [:card1, :card2]
    game.deal_hand player
    deck.was told_to(:pop).with(2)
    player.hands.last.should == Hand.new(:card1, :card2)
  end
end

describe Deck do
  it 'is substitutable for the mock' do
    # because we use the mock in tests
    # we want to make sure its interface matches the real deck
    Deck.should substitute_for MockDeck
  end
end
于 2013-03-24T08:12:50.920 回答
1

你考虑过使用mocha吗?

这将允许您对 Deck 进行存根或模拟,以确保它具有您的测试运行所需的卡片。

于 2013-03-24T07:42:04.970 回答
1

在您的测试中使用方法 instance_variable_set,它是对象上的 ruby​​ 方法。

所以我假设你的方法在 Game 类中,所以如果你正在设置类似的东西

@test_deck = something_that_sets_up_state_of_test_deck

@game = Game.new
@game.instance_variable_set(:deck, @test_deck

这将在 Game 中设置您的实例变量,而无需显式构建 attr_accessible 或 getter 和 setter。

于 2013-03-24T07:54:37.407 回答