3

I'm trying to implement an integration test framework using nose. At the core, I'd like a base class that all test classes inherit. I'd like to have a class setup function that is called as well as the per test setup function. When I use nosetests a_file.py -vs where a_file.py looks like this:

    from nose import tools

    class BaseClass(object):
        def __init__(self):
            print 'Initialize Base Class'

        def setup(self):
            print "\nBase Setup"

        def teardown(self):
            print "Base Teardown"

        @tools.nottest
        def a_test(self):
            return 'This is a test.'

        @tools.nottest
        def another_test(self):
            return 'This is another test'

    class TestSomeStuff(BaseClass):
        def __init__(self):
            BaseClass.__init__(self)
            print 'Initialize Inherited Class'

        def setup(self):
            BaseClass.setup(self)
            print "Inherited Setup"

        def teardown(self):
            BaseClass.teardown(self)
            print 'Inherited Teardown'

        def test1(self):
            print self.a_test()

        def test2(self):
            print self.another_test()

Outputs this:

Initialize Base Class
Initialize Inherited Class
Initialize Base Class
Initialize Inherited Class
cases.nose.class_super.TestSomeStuff.test1 ... 
Base Setup
Inherited Setup
This is a test.
Base Teardown
Inherited Teardown
ok
cases.nose.class_super.TestSomeStuff.test2 ... 
Base Setup
Inherited Setup
This is another test
Base Teardown
Inherited Teardown
ok

----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

How do I make the __init__, setup, and teardown functions class methods? When I attempt this:

from nose import tools

class BaseClass(object):
    def __init__(self):
        print 'Initialize Base Class'

    @classmethod
    def setup_class(self):
        print "\nBase Setup"

    @classmethod
    def teardown_class(self):
        print "Base Teardown"

    @tools.nottest
    def a_test(self):
        return 'This is a test.'

    @tools.nottest
    def another_test(self):
        return 'This is another test'

class TestSomeStuff(BaseClass):
    def __init__(self):
        BaseClass.__init__(self)
        print 'Initialize Inherited Class'

    @classmethod
    def setup_class(self):
        BaseClass.setup_class(self)
        print "Inherited Setup"

    @classmethod
    def teardown_class(self):
        BaseClass.teardown_class(self)
        print 'Inherited Teardown'

    def test1(self):
        print self.a_test()

    def test2(self):
        print self.another_test()

I get this:

Initialize Base Class
Initialize Inherited Class
Initialize Base Class
Initialize Inherited Class
ERROR

======================================================================
ERROR: test suite for <class 'cases.nose.class_super.TestSomeStuff'>
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/nose/suite.py", line 208, in run
    self.setUp()
  File "/usr/lib/python2.7/dist-packages/nose/suite.py", line 291, in setUp
    self.setupContext(ancestor)
  File "/usr/lib/python2.7/dist-packages/nose/suite.py", line 314, in setupContext
    try_run(context, names)
  File "/usr/lib/python2.7/dist-packages/nose/util.py", line 478, in try_run
    return func()
  File "/home/ryan/project/python_testbed/cases/nose/class_super.py", line 30, in setup_class
    BaseClass.setup_class(self)
TypeError: setup_class() takes exactly 1 argument (2 given)

----------------------------------------------------------------------
Ran 0 tests in 0.001s

FAILED (errors=1)

Removing the self from the super class calls (BaseClass.setup_class(self) -> BaseClass.setup_class()) seems to fix it...which I don't understand:

Initialize Base Class
Initialize Inherited Class
Initialize Base Class
Initialize Inherited Class

Base Setup
Inherited Setup
cases.nose.class_super.TestSomeStuff.test1 ... This is a test.
ok
cases.nose.class_super.TestSomeStuff.test2 ... This is another test
ok
Base Teardown
Inherited Teardown

----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

However, this doesn't help with the __init__ function. How can I make this a class method? Why does passing in self to the super class fail?

Does anyone have some info on this?

Vikrant
  • 4,920
  • 17
  • 48
  • 72
rjbez
  • 802
  • 2
  • 12
  • 21

2 Answers2

2

Class methods take a single implicit argument, (called cls by convention, although you have called it self too), like instance methods take self.

When you call

BaseClass.setup_class(self)

It's really more like

BaseClass.setup_class(BaseClass, self)

hence the warning over two arguments. Therefore it's fixed when you ditch self; as a reminder, change the definitions:

@classmethod
def setup_class(cls):

Oh, and __init__ makes no sense as a @classmethod; it's for setting up instances.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • You might find this question useful: http://stackoverflow.com/questions/1269217/calling-a-base-classs-classmethod-in-python – jonrsharpe Jan 27 '14 at 22:20
  • I understand that __init__ makes no sense as a class method, but it is the __init__ function for the object...the fact that it is ran 2 times tells me that nose is creating a new object for each test...is this true? – rjbez Jan 28 '14 at 13:24
  • Yes, it appears to be creating two instances of the `TestSomeStuff` class – jonrsharpe Jan 28 '14 at 13:32
  • I guess I'll go with it...I don't really understand why it creates an object for each test, or why it doesn't just pass `self` instead of this `cls` argument. Is there any documentation on this I can read up on? Am I missing something in the nose docs? – rjbez Jan 28 '14 at 13:44
  • For information on classes, instances, class methods, see [here](http://docs.python.org/2/reference/datamodel.html#types). There is more information on what happens internally to `nosetest` [here](https://nose.readthedocs.org/en/latest/api.html). – jonrsharpe Jan 28 '14 at 13:57
1

Looks like @Jonrsharpe already beat me, but yeah. I'll post it anyway.

This one might explain the setup_class() error. It is passing in an instance of BaseClass as well as 'self' (or TestSomeStuff) when you include the self. Nose must be hard coded to not allow more then 1 parameter in that function (or Unittest, not sure which one has that requirement).

As for the init part, after reading through the Unittest documentation, it appears that the code would be better printed as:

def __init__(self):
        print 'Initialize Base Class Object'

And:

def __init__(self):
    BaseClass.__init__(self)
    print 'Initialize Inherited Class Object'

Since it's basically creating an object for every test case and running that init function to make sure the test case is ready.

Community
  • 1
  • 1
Rooks103
  • 62
  • 6