How to access class property in decorator in Python?

1.6k views Asked by At

I am trying to use a nose_parameterized test and want to use it for a unittest method.

from nose.tools import assert_equal
from nose_parameterized import parameterized
import unittest

Class TestFoo(unittest.TestCase):
      def setUp(self):
           self.user1 = "Bar"
           self.user2 = "Foo"

      @parameterized.expand([
               ("testuser1",self.user1,"Bar"),
               ("testuser2",self.user2,"Foo")
                ]
      def test_param(self,name,input,expected):
          assert_equal(input,expected)

But self is not defined in the decorator function. Is there a workaround for this? I know that I can use global class variables but I need to use variables in setUp.

2

There are 2 answers

3
jme On BEST ANSWER

One workaround would be to use a string containing the attribute name in the decorator, and getattr in the test function:

@parameterized.expand([
           ("testuser1", "user1", "Bar"),
           ("testuser2", "user2", "Foo")
            ])
def test_param(self, name, input, expected):
    assert_equal(getattr(self, input), expected)

With this approach, test_param assumes that the value of its input argument is the attribute name whose value should be checked against expected.

0
jez On

The decorator is not run when you seem to assume it will be run. In the following example:

class spam:
    @eggs
    def beans( self ):
        pass

remember that the use of the decorator is the same as saying:

beans = eggs( beans )

inside the spam scope, immediately after the def statement itself is executed. When is a def statement executed? At the time the class and its methods are defined. The decorator modifies the class-level definition of the method spam.beans, not the instance-level value of self.beans. And of course, this occurs long before any instances of that class are ever created, i.e. before a reference to any one particular instance, self, is ever meaningful.

If you want to attach a particular callable (e.g. a modified test_param callable that has certain arguments pre-baked into it using functools.partial) to an instance self, you can of course do so inside one of the instance methods (e.g. __init__ or setUp).

Some people will describe the class-definition code as happening at "parse time" and instance-level code as happening at "run time". You may or may not find that a helpful way of thinking about it, although really almost everything is "run-time" in Python.