sunwukung
sunwukung

Reputation: 2815

strange object instance behaviour in unittest

I'm trying to test a class using Python unittest, but the test suite seems to be modifying the base class

here's some pseudo code to demonstrate the problem:

the class

// module cards...
class deck():

    def __init__(self, cards):
        self.__cards = cards

    def __len__(self):
        return len(self.__cards)

    def draw(self, n = 1):
        '''
        remove and return n cards
        from internal card list

        n -- integer
        @return list removed
        '''
        removed      = self.__cards[0:n]
        remaining    = self.__cards[n:]
        self.__cards = remaining
        return removed

    def addOne(self, card):
        '''
        add single card to the deck
        '''
        self.__cards.append(card)

    def addMany(self, cards):
        '''
        add many cards to the deck
        '''
        self.__cards.extend(cards)

a fixture to populate the instance, will probably be the output of a file or DB query

//source
src = [
{
    'id'          : 1,
    'name'        : 'one',
    'description' : 'this is one'
},
{
    'id'          : 2,
    'name'        : 'two',
    'description' : 'this is two'
},
{
    'id'          : 3,
    'name'        : 'three',
    'description' : 'this is three'
},
{
    'id'          : 4,
    'name'        : 'four',
    'description' : 'this is four'
},
{
    'id'          : 5,
    'name'        : 'five',
    'description' : 'this is five'
}
]

the tests

from source import src
import cards 

def test_drawRemovesOne(self):
    deck = cards.deck(src)
    self.assertTrue(callable(deck.draw))

    # single card
    deckSize  = len(deck)
    drawnCard = deck.draw(1)
    self.assertEqual(drawnCard[0]['id'], 1)
    self.assertEqual(4, len(deck))
    self.assertEqual(len(drawnCard), 1)



def test_drawRemovesMany(self):
    deck = cards.deck(src)
    deckSize   = len(deck)
    drawnCards = deck.draw(3)
    self.assertEqual(drawnCards[0]['id'], 1)
    self.assertEqual(drawnCards[1]['id'], 2)
    self.assertEqual(drawnCards[2]['id'], 3)
    self.assertEqual(len(deck), 2)
    self.assertEqual(len(drawnCards), 3)


'''
INVALIDATES PREVIOUS TESTS...
def test_addOne(self):
    deck = cards.deck(src)
    card = {
        'id'          : 9,
        'name'        : 'nine',
        'description' : 'this is nine'
    }
    deckSize = len(deck)
    deck.addOne(card)
    newDeckSize = len(deck)
    self.assertTrue(newDeckSize == deckSize + 1)
'''

EDIT: posted real snippet

the first test passes, but then subsequently fails when second test is implemented. It seems as if there is one reference to the class being shared by the test methods, despite appearing to be fresh instances.

I've only done a nominal bit of work in Python, is this the result of some language peculiarity I'm unaware of?

Upvotes: 1

Views: 153

Answers (1)

Facundo Casco
Facundo Casco

Reputation: 10605

See this code

>>> class Deck:
...     def __init__(self, cards):
...         self.cards = cards
>>> l0 = [1, 2, 3]
>>> d1 = Deck(l0)
>>> d2 = Deck(l0)
>>> d1.cards
0: [1, 2, 3]
>>> d2.cards
1: [1, 2, 3]
>>> d1.cards.append(4)
>>> d2.cards
2: [1, 2, 3, 4]    # d1 and d2 share the same list for cards

You start both your tests with deck = cards.deck(src) but I don't know where that src is coming from. If it isn't created in your setUp method as a new object then it could be being shared between the test cases.

Upvotes: 1

Related Questions