3

I want to run several unittests on a class with some class level variables. The class level variables are not reset back to pre-run values as the unittest code keeps a reference to the class. Other than resetting all class level variables in an init method in the class under test how can I get a new class for each unittest method?

class NonEmptyClassTest(unittest.TestCase):

    def test_makeName(self):
        nec = NonEmptyClass()
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

    def test_makeName_1(self):
        nec = NonEmptyClass()
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))


class NonEmptyClass(object):
    dummy_data = {}

    def printAllData(self):
        for k,v in self.dummy_data.items():
            print k, v

    def addName(self, name):
        if not name in self.dummy_data:
            self.dummy_data[name] = name+"_value"
        else:
            name = name + ".1"
            self.dummy_data[name] = name+"_value"

4 Answers 4

4

You can use setUp() to clear the dictionary and make all test cases independent. That are unit tests and it is a good idea that they not depend from each other and from run order.

class NonEmptyClassTest(unittest.TestCase):
    def setUp(self):
        NonEmptyClass.dummy_data = {}

    def test_makeName(self):
        nec = NonEmptyClass()
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

    def test_makeName_1(self):
        nec = NonEmptyClass()
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

[EDIT]

From the comment. IMHO write explicitly what are the class variable and how reset it make the test more clear and give to you some more useful check points.

Of course, if you add more class variables you MUST update your setUp() test case but I think that is a plus. Tests must be clear and should not hide behavior like these.

Sign up to request clarification or add additional context in comments.

1 Comment

He is speaking about a class with some class level variables so he must clean all class level variables. Did it in a explicit way make the test clear and simple: moreover remember in the test what are the class variables.
2

It is the module that the class is defined in, that keeps a reference. And the module itself is kept in sys.modules to prevent repeatedly running the module top-level code each time you use it in another module through the import statement.

Make sure your unit test is defined in a separate module, and move importing of the class into a helper function, where you ensure that the module is deleted first before importing. This ensures that the module is re-created each time:

import sys

class NonEmptyClassTest(unittest.TestCase):
    def _makeOne(self, clear=False):
        if clear:
            try:
                del sys.modules['module_name']
            except KeyError:
                pass
        from module_name import NonEmptyClass
        return NonEmptyClass

    def test_makeName(self):
        nec = self._makeOne(clear=True)
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

    def test_makeName_1(self):
        nec = self._makeOne(clear=True)
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

The clear keyword argument lets you create more than one instance without clearing the module out, to test that the data is indeed being shared between instances.

Comments

1

You should refrain from using mutable objects on class variables. Please refer to this tutorial: https://docs.python.org/2/tutorial/classes.html#class-and-instance-variables.

As mentioned by @markcial on his original unedited answer, you have to have to make your dummy_data an instance variable instead of a class variable.

Trying to tweak your tests to pass instead by doing some setUp method hacks and variable re-initializations is a sign that the underlying implementation of the class you are actually testing has an implementation flaw.

So you rather fix the the class instead of wrestling with its unit test. Hence I am copy and pasting @markcial's original answer here which only fixes the implementation of the class:

class NonEmptyClass(object):

    def __init__(self):
        self.dummy_data = {}

    def printAllData(self):
        for k, v in self.dummy_data.items():
            print k, v

    def addName(self, name):
        if name not in self.dummy_data:
            self.dummy_data[name] = name + "_value"
        else:
            self.dummy_data[name] = name + ".1_value"

3 Comments

There are legitimate reasons to use mutable class attributes - "don't use class attributes" is just like any "golden rule": don't do it - unless you really understand why it might be a bad idea ;)
By should refrain from i intended to mean should resist to .... Its like using mutable variables as default arguments to a function. It is more harmful than useful. And chances are, even in this case, using a mutable class variable is not meant for some special reason but just an oversight.
we obviously agree on the fact that mutable class attributes are most often a bad idea - but it doesn't mean there are no legitimate reasons to use them.
-1

if you want your class to have the values defined on class instantiaton add the __init__ method on the class:

class NonEmptyClass(object):

    def __init__(self):
        self.dummy_data = {}

    def printAllData(self):
        for k,v in self.dummy_data.items():
            print k, v

    def addName(self, name):
        if not name in self.dummy_data:
            self.dummy_data[name] = name+"_value"
        else:
            name = name + ".1"
            self.dummy_data[name] = name+"_value"

EDIT

If its only test based use the setup to redefine variable from class object :

class NonEmptyClassTest(unittest.TestCase):

    nec = None

    def setUp(self):
        NonEmptyClass.dummy_data = {}
        self.nec = NonEmptyClass()

    def test_makeName(self):
        self.nec.addName("Fred")
        self.nec.printAllData()
        self.assertEquals(1 , len(self.nec.dummy_data))

    def test_makeName_1(self):
        self.nec.addName("Fred")
        self.nec.printAllData()
        self.assertEquals(1 , len(self.nec.dummy_data))

1 Comment

This rather defeats the whole point of the class-level variable. The OP wants the class to share the dictionary. Just not in unit tests.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.