Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testdata attrs that are referenced in a classmethod will have different values at test runtime #13

Closed
wolfadactyl opened this issue Sep 16, 2021 · 1 comment

Comments

@wolfadactyl
Copy link

The issue description here is admittedly terrible, so here is an example of a test that will fail to make it clearer:

class TestFoo(TestCase):
    @classmethod
    @wrap_testdata
    def setUpTestData(cls):
        super().setUpTestData()
        cls.foo = 'bar'

    @classmethod
    def validate(cls):
        assert cls.foo == 'baz'

    def test_baz(self):
        self.foo = 'baz'
        self.validate()

There are times when a classmethod makes sense - e.g. a classmethod can be called in setUpTestData whereas an instance method cannot. While this implementation is using a basic string attr, it's more critical in the case of an object attr where we may want to call that classmethod again at test runtime, however with wrap_testdata the value of the attr is the original state that was set in setUpTestData and doesn't include the changes made in the test itself.

We could refresh the value in the classmethod, however that would break wrap_testdata as now the class will have the modified version from the test going forward.

@charettes
Copy link
Owner

charettes commented Sep 18, 2021

however with wrap_testdata the value of the attr is the original state that was set in setUpTestData and doesn't include the changes made in the test itself.

That's just how attribute assignment works in Python, if you do assert self.foo == 'baz' in test_baz you'll notice that the assignment worked properly.

class cls:
    foo = 'bar'

assert cls.foo == 'bar'
self = cls()
assert self.foo == 'bar'  # Since self.__dict__['foo']  is missing it falls back to cls.__dict__['foo']
self.foo = 'baz'  # Assigns self.__dict__['foo'] = 'baz' and leaves cls.__dict__['foo']  untouched
assert cls.foo == 'bar'
assert self.foo == 'baz'

In the example you provided, even with the decorator removed, the attribute assignment flow is the following:

  1. setUpTestData; TestFoo.__dict__['foo'] = 'bar'
  2. test_baz; <instance TestFoo>.__dict__['foo'] = 'baz'
  3. vaidate; assert TestFoo.__dict__['foo'] == 'baz' which fails

In other words, doing self.foo = 'baz' doesn't alter TestFoo.foo in any way (that's just how class instance attribute assignment works in Python) and the testdata descriptor won't interfere with that at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants