2

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.

jez
  • 14,867
  • 5
  • 37
  • 64
Mahsa Mortazavi
  • 755
  • 3
  • 7
  • 23
  • Related: http://stackoverflow.com/questions/7590682/access-self-from-decorator and http://stackoverflow.com/questions/11731136/python-class-method-decorator-w-self-arguments. – alecxe Dec 11 '15 at 18:20

2 Answers2

5

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.

jme
  • 19,895
  • 6
  • 41
  • 39
  • To make this work, either the assignments to `self.user1` and `self.user2` in the OP's code should be modified to happen inside `__init__(self)`, or `user1` and `user2` should become class attributes. In the latter case your code would have to become `getattr(TestFoo, input)` but I assume the OP wants the former (otherwise why not just hard-code the `input` values "Foo" and "Bar" into the decorator). – jez Dec 11 '15 at 18:36
  • @jez I believe this works as-is. `self.user1` and `self.user2` are assigned in the `setUp` method which is run before every test. – jme Dec 11 '15 at 18:45
  • ah, I see, then it's just a case of the keyword `def` missing before `setUp` in the OP's code. – jez Dec 11 '15 at 19:07
3

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.

jez
  • 14,867
  • 5
  • 37
  • 64