4

How would I stub the output of pickCard() function which is being called twice in deal() function? I want to test both losing and winning cases.
For example, I would like to have for the winning case, the first time pickCard() is called is given value 8 to card1, and second given value 10 to card2.

I have tried using @Mock.patch, but this works only for doing one call.

I have used self.blackjack.pickCard = MagicMock(return_value=8) but again if i use it twice it will overwrite the return value

Here is the code:

import random

class Game:
    def __init__(self):
        self.cards = [1,2,3,4,5,6,7,8,9,10]

    def deal(self):
        card1 = self.pickCard()
        self.removeCards(card1)
        card2 = self.pickCard()
        return card1  + card2  > 16

    def pickCard(self):
        return random.choice(self.cards)

    def removeCards(self,card1):
        return self.cards.remove(card1)

The test file is:

import unittest
from mock import MagicMock
import mock
from lib.game import Game

class TestGame(unittest.TestCase):
    def setUp(self):
        self.game = Game()

    def test_0(self):#passing
        """Only cards from 1 to 10 exist"""
        self.assertListEqual(self.game.cards, [1,2,3,4,5,6,7,8,9,10])

    #Here is where I am finding difficulty writing the test
    def test_1(self):
        """Player dealt winning card"""
        with mock.patch('lib.game.Game.pickCard') as mock_pick:
            mock_pick.side_effect = (8, 10)
            g = Game()
            g.pickCard()
            g.pickCard()
            self.assertTrue(self.game.deal())

EDIT

I ran this test with above code, and I get this stack trace instead of passing

Traceback (most recent call last):
 tests/game_test.py line 26 in test_1
   self.assertTrue(self.game.deal())
 lib/game.py line 8 in deal
   card1 = self.pickCard()
 /usr/local/lib/python2.7/site-packages/mock/mock.py line 1062 in __call__
   return _mock_self._mock_call(*args, **kwargs)
 /usr/local/lib/python2.7/site-packages/mock/mock.py line 1121 in _mock_call
   result = next(effect)
 /usr/local/lib/python2.7/site-packages/mock/mock.py line 109 in next
   return _next(obj)

Do I need to put the two g.pickCard() elsewhere in the test? Or do I need to need to access this in the self.game.deal() method somehow?

cani
  • 210
  • 3
  • 10

1 Answers1

3

mock.patch is the way to go, but instead of return_value you should specify side_effect=(8, 10)

with mock.patch('lib.game.Game.pickCard') as mock_pick:
    mock_pick.side_effect = (8, 10)
    g = Game()
    print(g.pickCard())
    print(g.pickCard())

# 8
# 10

EDIT #1

Pick card was just for demonstration that different cards are picked. In your code you pick both cards and then call game.deal which picks another two cards which are not mocked which raises StopIteration. Also, since your game object already exists (created in setUp) you should patch this object directly, not create a new game object, hence your test_1 should be:

def test_1(self):
    """Player dealt winning card"""
    with mock.patch.object(self.game, 'pickCard') as mock_pick:
        mock_pick.side_effect = (8, 10)
        self.assertTrue(self.game.deal())

You path object's property pickCard with MagicMock and set it's side effects to 8 and 10 respectively.

warownia1
  • 2,771
  • 1
  • 22
  • 30
  • 1
    https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.side_effect – dm03514 Aug 09 '16 at 14:40
  • If you need something way more sophisticated, which controls the behavior of `mock_pick` at each call, see my answer here: http://stackoverflow.com/questions/26783678/python-mock-builtin-open-in-a-class-using-two-different-files/38618056#38618056 – warownia1 Aug 09 '16 at 14:44
  • Thanks for that @warownia1, I did see side effect elsewhere, but was not too sure how to use it. I am still getting issues with using the stubbed methods, see above in OP. – cani Aug 09 '16 at 18:02
  • Edited to suit your particular case. My first answer was a generic example how to patch functions when multiple calls are required. – warownia1 Aug 10 '16 at 09:28