2013-03-24 28 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

相關問題