Using mocks to observe behaviors

The preceding mock objects were used to test how a Deck class was built. Having 52 identical sentinels makes it difficult to confirm that a Deck deals properly. We'll define a different mock to test the deal feature.

Here's a second test case to ensure that the Deck class deals properly:

class TestDeckDeal(unittest.TestCase):

def setUp(self) -> None:
self.mock_deck = [
getattr(unittest.mock.sentinel, str(x)) for x in range(52)
]
self.mock_card = unittest.mock.Mock(
side_effect=self.mock_deck)
self.mock_rng = unittest.mock.Mock(
wraps=random.Random())
self.mock_rng.shuffle = unittest.mock.Mock()

def test_Deck3_should_deal(self) -> None:
d = Deck3(size=1, random=self.mock_rng, card_factory=self.mock_card)
dealt = []
for i in range(52):
card = d.deal()
dealt.append(card)
self.assertEqual(dealt, self.mock_deck)

def test_empty_deck_should_exception(self) -> None:
d = Deck3(size=1, random=self.mock_rng, card_factory=self.mock_card)
for i in range(52):
card = d.deal()
self.assertRaises(DeckEmpty, d.deal)

This mock for the card factory function uses the side_effect argument to Mock(). When provided with an iterable, the side_effect feature returns another value of the iterable each time it's called.

In this case, we used the sentinel object to build 52 distinct sentinel objects; we'll use these instead of Card objects to isolate the Deck3 class from the Card class hierarchy. The getattr(unittest.mock.sentinel, str(x)) expression will use a string version of a number, x, and create 52 unique sentinel objects.

We mocked the shuffle() method to be sure that the cards aren't actually rearranged. The wrapper means that most features of the Random class will be accessible. The shuffle method, however, was replaced. We want the sentinel objects to stay in their original order so that our tests have a predictable expected value.

The first test, test_Deck3_should_deal, accumulates the results of dealing 52 cards into a variable, dealt. It then asserts that this variable has the 52 expected values from the original mock card factory. Because the card factory was a mock object, it returned the various sentinel objects via the side_effect feature of Mock.

The second test, test_empty_deck_should_exception, deals all of the cards from a Deck instance. However, it makes one more API request. The assertion is that the Deck.deal() method will raise the proper exception after dealing all of the cards.

Because of the relative simplicity of the Deck class, it's possible to combine both TestDeckBuild and TestDeckDeal into a single, more sophisticated mock. While that's possible with this example, it's neither essential, nor necessarily desirable to refactor the test cases to make them simpler. Indeed, the overs simplification of tests may fail to properly test API features.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset