pythonunit-testingscopeinstallationteardown

unittest's tearDown and setUp don't update the properties the same way


I'm writing some unit tests for which I have some preparation to do before every test, and which I should undo after each test. So I'm using setUp to prepare them, using a list to list the actions I've performed, and in the tearDown, I undo the operations listed in said list and clear the list doing so.

But when I reach the following test, the list is not in the same state as tearDown left it, but at the state previous setUp left it. And it feels weird. Am I doing something wrong here?

Here is an short example, without my business code:

class MyTest(TestCase):
    val = []

    def setUp(self):
        super().setUp()
        print("before setup, val = {}".format(self.val))

        self.val.append("AAA")

        print("after setup: val = {}".format(self.val))

    def tearDown(self):
        super().tearDown()
        print("before teardown, val = {}".format(self.val))

        self.val = []  # or whatever

        print("after teardown, val = {}".format(self.val))

    def test_one(self):
        assert True

    def test_two(self):
        assert True

Will return

before start, val = []
after setup: val = ['AAA']
before teardown, val = ['AAA']
after teardown, val = []
.
before start, val = ['AAA']  # <- why is this not []??
after setup: val = ['AAA', 'AAA']
before teardown, val = ['AAA', 'AAA']
after teardown, val = []
.

Shouldn't the setUp from the second test find .val == []? The value seem to have been unaltered by previous tearDown, I don't know why. Any thoughts?


Solution

  • val is a class attribute. When you do:

    self.val.append("AAA")
    

    This modifies the class version of val. However when you do:

    self.val = []
    

    You create an instance attribute named val and set it to an empty list. What you really want to do here is:

    self.val[:] = []
    

    This will modify the mutable class attribute val.