测试方法依赖于基本上随机的实例变量

时间:2013-03-24 07:39:20

标签: ruby unit-testing

我正在做一个二十一点游戏。我的游戏对象包含一个甲板对象,在甲板达到一定的穿透水平后进行洗牌。我的许多方法都依赖于这个deck对象。我没有看到任何理由通过setter方法访问deck对象。我在Game类上测试方法时遇到了麻烦,因为它们依赖于甲板的顺序,这是随机的。

例如,我有deal_hand方法。

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

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

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

编辑:

我正在使用MiniTest,它支持存根/模拟。虽然据我所知,它只允许您为模拟对象上的方法调用设置预期的返回值。如果我做了一个模拟牌组,实际的牌组对象也取决于内部阵列。调用deck的代码都没有直接访问数组。

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

您是否考虑过使用mocha

这将允许您存根或模拟Deck以确保它具有用于测试运行的预期卡片。

答案 2 :(得分: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。

相关问题