2

I'm writing a package, and doing my testing like a good little programmer, but here's what happens:

class TestOne(unittest.TestCase):
    def setUp(self):
        self.finder = Finder()

    def test_default_search_parts(self):
        self.assertEqual(self.finder.search_parts, [])

class TestTwo(unittest.TestCase):
    def setUp(self):
        self.finder = Finder()

    def test_add_letter(self):
        self.finder.add('a')
        self.assertNotEqual(self.finder.search_parts, [])

in this case, test_default_search_parts fails with AssertionError: ['a'] != [], and test_add_letter passes. I don't know what's going on here. It gets really weird when I rewrite test_default_search_parts:

def test_default_search_parts(self):
    f = Finder()
    self.assertEqual(f.search_parts, [])

the same failure occurs. What am I doing wrong here with initializing my instances?

Oh, and I'm using nose to run them, if that matters.

Brian Hicks
  • 6,213
  • 8
  • 51
  • 77
  • Can you throw in the code for Finder? Keep in mind the *order* of tests is not consistent or guaranteed, so if these instances are sharing some global state things can go horribly awry in confusing ways. – Henry Apr 27 '11 at 17:50
  • Perhaps the `Finder` is storing `search_parts` as a class-level attribute? – samplebias Apr 27 '11 at 17:51
  • @samplebias exactly, I will post an example of the problem. – Henry Apr 27 '11 at 17:55

1 Answers1

4

As @samplebias mentioned, shared state, in this case with class-level attributes, can cause problems. Here is a possible situation you have:

import unittest

# bad implementation of Finder, class-level attribute
class Finder(object):
    search_parts = []

    def add(self, letter):
        self.search_parts.append(letter)


# using 'Zne' here makes sure this test is run second        
class TestZne(unittest.TestCase):
    def setUp(self):
        print 'I am run next'
        self.finder = Finder()

    def test_default_search_parts(self):
        self.assertEqual(self.finder.search_parts, [])


class TestTwo(unittest.TestCase):
    def setUp(self):
        print 'I am run first'
        self.finder = Finder()

    def test_add_letter(self):
        self.finder.add('a')
        self.assertNotEqual(self.finder.search_parts, [])

unittest.main()

Outputs

Traceback (most recent call last):
  File "test.py", line 18, in test_default_search_parts
    self.assertEqual(self.finder.search_parts, [])
AssertionError: Lists differ: ['a'] != []

The problem being that all Finder instances share this class-level attribute search_parts, and add_letter is being run before the default search test.

To resolve, use something like:

class Finder(object):
    def __init__(self):
        self.search_parts = []

This will ensure it is an instance attribute only.

Henry
  • 6,502
  • 2
  • 24
  • 30
  • 1
    See this question for a detailed explanation of the difference Henry is describing: http://stackoverflow.com/questions/207000/python-difference-between-class-and-instance-attributes – Wilduck Apr 27 '11 at 18:03