55

How do I persist changes made within the same object inheriting from TestCase in unitttest?

from unittest import TestCase, main as unittest_main


class TestSimpleFoo(TestCase):
    foo = 'bar'

    def setUp(self):
        pass

    def test_a(self):
        self.assertEqual(self.foo, 'bar')
        self.foo = 'can'

    def test_f(self):
        self.assertEqual(self.foo, 'can')


if __name__ == '__main__':
    unittest_main()

I.e.: I want those two tests above to pass

A T
  • 13,008
  • 21
  • 97
  • 158
  • 5
    Unit tests should be independent. [In any case, `self.foo` refers to an *instance variable* while `foo = 'bar'` (where it is located) assigns a *class variable*.] – user2864740 Jan 30 '14 at 04:24
  • 2
    I'm testing OAuth2; `login` sets an `access_token` which I require for the next few tests. – A T Jan 30 '14 at 04:26
  • 1
    Can't you create a *different* test-class with the appropriate `setUp` to mock the appropriate access_token? (Make the setUp call the OAuth as required; more of integration testing at this point .. but take it that the setUp *cannot be wrong*, or the other TestCase would have failed.) – user2864740 Jan 30 '14 at 04:28
  • See http://stackoverflow.com/questions/3843171/unit-testing-with-dependencies-between-tests - not really any answers, but some suggestions and links. – user2864740 Jan 30 '14 at 04:29
  • 3
    Consider looking into [`setUpClass()`](https://stackoverflow.com/q/8389639/3357935). It runs a single time when your class is called. You can use it to setup variables needed for multiple tests. – Stevoisiak Nov 06 '17 at 20:47

3 Answers3

71

As some comments have echoed, structuring your tests in this manner is probably a design flaw in the tests themselves and you should consider restructuring them. However, if you want to do this and rely on the fact that the test runner you are using executes them in an alphabetical (seemingly) order then I suggest the following.

Similar to what @Matthias was saying but I would do one thing differently for the cases where you may decide to inherit from the class at a later date.

from unittest import TestCase, main as unittest_main


class TestSimpleFoo(TestCase):
    foo = 'bar'

    def setUp(self):
        pass

    def test_a(self):
        self.assertEqual(self.__class__.foo, 'bar')
        self.__class__.foo = 'can'

    def test_f(self):
        self.assertEqual(self.__class__.foo, 'can')


if __name__ == '__main__':
    unittest_main()

The difference between this answer and @Matthias's answer you accepted is the explicit declaration of the class versus the lookup of said class reference.

TestSimpleFoo vs self.__class__

I prefer the dynamicness so I can inherit the tests later and run both test classes back to back and not have any cross over between the two. Because if you would choose to inherit from this class, explicitly naming the class reference would cause both test classes to run against that reference rather than their own respective classes.

rdp
  • 2,675
  • 2
  • 21
  • 21
  • 1
    Yeah, I've been using the whole `def test_0` syntax to overcome the sorting issues. Sure, will use `__class__`; which is also more concise =). Yes, I could restructure my tests; but would rather a cleaner test interface. – A T Jul 25 '14 at 01:41
15

I like your own answer for the simplicity of it, but if you want to keep distinct unit tests:

Apparently unittest runs separate tests with fresh instances of the TestCase. Well, just bind the objects to be persisted to something else but self. For example:

from unittest import TestCase, main as unittest_main


class TestSimpleFoo(TestCase):

    def setUp(self):
        pass

    def test_a(self):
        TestSimpleFoo.foo = 'can'

    def test_f(self):
        self.assertEqual(TestSimpleFoo.foo, 'can')


if __name__ == '__main__':
    unittest_main()

You might be interesed in setUpClass and tearDownClass too: https://docs.python.org/3/library/unittest.html#setupclass-and-teardownclass

Also take care about the execution order of your unit tests: https://docs.python.org/2/library/unittest.html#unittest.TestLoader.sortTestMethodsUsing

Matthias
  • 1,296
  • 8
  • 17
  • Thanks, I am already using `@tearDownClass` for my `unregister` function. Interesting approach you're suggestion: will give it a go. – A T Jul 18 '14 at 12:20
  • 1
    You're not supposed to do this. What if your test runner decides to run tests in separate processes (for e.g. distributing load to multiple computers) ? There are certain assumptions that test runners make about unit test and you should not violate them. As Ned says, A T's answer is the right way to do it. – Burak Arslan Jul 22 '14 at 08:16
  • 1
    I dont like this solution, it should not be necessary to structure your tests this way. I think the most viable solution is to restructure/design your tests, so they conform with the conseptional ideas of python unit testing. – brunsgaard Jul 23 '14 at 10:23
  • @brunsgaard I can relate. It's a workaround and as such not very beautiful. The underlying problem is that python unit testing doesn't support the concept of test sequences. Test sequences - of course - are only the second best solution to indepedent unit tests, but in the face of the trade-off between test run time and test maintainability a valid solution. – Matthias Jul 24 '14 at 05:48
  • @Matthias, You may take a look at http://pytest.org, I don't know it, but people are talking about it here at EuroPython. Maybe it will do something good for you :) Talk from yesterday https://www.youtube.com/watch?v=LdVJj65ikRY – brunsgaard Jul 24 '14 at 09:00
  • 1
    @brunsgaard Thx, "Sharing a fixture across tests in a module" is exactly what the OP needs: http://pytest.org/latest/fixture.html#sharing-a-fixture-across-tests-in-a-module-or-class-session – Matthias Jul 25 '14 at 06:09
2

Couldn't figure it out; so ended up hacking it out with multiple non test_ prefixed functions:

def test_password_credentials_grant(self):
    for user in self.user_mocks:
        self.register(user)
        self.login(user)
        self.access_token(user, self.assertEqual)  # Ensures access_token is generated+valid
        self.logout(user)
        self.access_token(user, self.assertNotEqual)  # Ensures access_token is now invalid
        self.unregister(user)
A T
  • 13,008
  • 21
  • 97
  • 158