2013-03-24 68 views
2

我正在进行二十一点游戏。我的游戏对象包含一个甲板对象,在甲板达到一定的穿透水平后被洗牌。我的许多方法都依赖于这个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,支持存根/嘲讽。尽管据我所知,它只允许您为模拟对象上的方法调用设置预期返回值。如果我制作了一个模拟套牌,实际的套牌对象也依赖于一个内部数组。调用卡组的代码都不直接访问数组。

+0

如何在第一个地方的@deck设置? – 2013-03-24 07:51:08

+0

你打开一个重构建议,可以使这个更容易测试?如果不是这样的话:-) – 2013-03-24 08:05:38

+0

是的,我会的。 @deck是一个Card对象的数组,已随机随机播放!当Game.new被调用时它被设置。 – 2013-03-24 08:07:17

回答

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

您是否考虑过使用mocha

这将允许您存根或模拟甲板,以确保它具有您的测试运行的预期卡。

+0

我正在使用minitest。我刚刚编辑以添加更多信息。 – 2013-03-24 07:44:40

1

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

所以我假设你的方法是在游戏类,所以你如果你设置像

@test_deck = something_that_sets_up_state_of_test_deck 

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

,将设置游戏内的实例变量的东西,而不需要attr_accessible或getter和制定者正在明确建立。

+1

这是你的问题的正确答案。然而,所有这些都说了,我想你可能想看看更多基于模拟和存根的测试样式。嘲笑甲板对象并在模拟上存留方法以返回任何需要的东西,以便真正可以单独测试deal_hand方法。思考如何做到这一点也将帮助你解耦你的逻辑 - 你的方法必须知道很多东西来完成它的工作 - 像你一样的链式方法可能是一种代码气味,你在对象之间也有耦合。 – 2013-03-24 08:01:00

相关问题