1

Let's say I have a module named foo with a class Bar. Bar has a classwide counter attribute that allows me to track the order which instances were created. foo looks like this:

from itertools import count

class Bar:
    class_count = count(0)
    def __init__(self):
        self.id = self.class_count.next()

Now I have a test file where I am testing the various functionalities of Bar. I am unsure of how to test this id attribute, because the other unittests are creating instances of Bar and so I don't know what the given id of a Bar instance should be. Furthermore, this behavior of my class means that my unittests are independent of each other, which is undesirable. How should I structure my unittests so the tests are independent of each other?

C_Z_
  • 7,427
  • 5
  • 44
  • 81
  • 1
    This most likely answers your question: https://stackoverflow.com/questions/7460363/re-import-module-under-test-to-lose-context – Blender May 21 '17 at 03:44
  • 2
    The question here is: what to test? Basically you only need to test that the counter's next method is called. Testing the function of the counter is not your job. So, mock the counter and assert if the mock is called as expected. – Klaus D. May 21 '17 at 03:48

2 Answers2

1

You could use setUp to safe the current count and then temporarily reset the count. Then with tearDown you restore the original state again:

from itertools import count
import unittest

class Bar:
    class_count = count(0)
    def __init__(self):
        self.id = next(self.class_count)


class TestBar(unittest.TestCase):
    def setUp(self):
        self.nxtcount = next(Bar.class_count)  # safe current state
        Bar.class_count = count(0)             # reset to 0

    def tearDown(self):
        Bar.class_count = count(self.nxtcount) # reset to old state

    def teststh1(self):
        x = Bar()
        self.assertEqual(x.id, 0)

    def teststh2(self):
        x1 = Bar()
        x2 = Bar()
        x3 = Bar()
        self.assertEqual(x1.id, 0)
        self.assertEqual(x2.id, 1)
        self.assertEqual(x3.id, 2)

This makes sure every test method will start with a Bar.class_count of 0.

MSeifert
  • 145,886
  • 38
  • 333
  • 352
0

I would stub out Bar to bypass the constructor.

class BarStub(Bar):
  def __init__(self):
    self.class_count = None
    self.id = None

Now you can test like this:

class TestBar(...):
  def setUp(...)
    ...
    self.bar = BarStub()

  def test_foo_should_blah_when_blah(self):
    with mock.patch.object(self.bar, 'count_class', side_effect=[...]) as mock_count:
      actual = self.bar.unit_under_test(...)
      mock_count.assert_called_with([...])
Dan
  • 1,874
  • 1
  • 16
  • 21