开发者

Initialization of unit-test in PyDev?

I am unit-testing my python code in eclipse using PyDev unit-testing. I right click on the appropriate file and select Ru开发者_开发技巧n As -> Python unit-test. Concerning this plugin I have a few questions:

  1. Is there a way to have a setUpClass method that is being executed before any other test within this class? Currently I am only able to get setUp working, which is called before any test of the class
  2. Is there a way to have a global initialization that is being called before any test is executed? Something like setUpModule which I am also not able to get running using PyDev unit-testing.

Thanks in advance for any answer and comment^^

Cherio Woltan

Example:

class TestClass(unittest.TestCase):

  @classmethod
  def setUpClass(self):
      print "Setup"    

  def test1(self):
      print "Test1"
  def test2(self):
      print "Test2"

If I run this with Run As -> Python unit-test the setUpClass method is not being called.


It's a PyDev bug, and has been fixed in 2.0.1.

setUpModule(), tearDownModule(), setUpClass(), and tearDownClass() are not run in the 'Python unit-test' run configuration due to the bug in PyDev 2.0.0 and earlier. In 2.0.1 they run correctly in the 'Python unit-test' and 'Python Run' configurations. I tested it myself to verify.


OK I'll give this a shot: I use Pydev and have been exploring using "nosetests" to launch tests so it's sort of relevant. My solution is a total hack, but does seem to work when saying "Debug as UnitTest" from within PyDev:

print "before any tests (global) just once..."
class MyTestCase(unittest.TestCase):
   class_setup_called = False
   def __init__(self, test_name):
      unittest.TestCase.__init__(self, test_name)
      if not self.__class__.class_setup_called:
          self.setUpClass()
          self.__class__.class_setup_called = True
   @staticmethod
   def setUpClass():
      print "before first test only..."
   def setUp(self):
      print "before each test..."

Unfortunately this will not work when using nosetests, but it does work when running from pydev. I'm guessing that nosetests is coded to instantiate test objects before each test method is run, in which case your init method would suffice.

I can't say enough nice things about nosetests:

  • support for exceptions and timed tests.
  • support for annotation "attribution".
  • integration with profile, coverage and xunit reporting of results.
  • recursive discovery and attributed execution of test cases.

examples:

import unittest
@raises(TypeError)          
def test_forexceptions(self): 
    pass

@attr('benchmark')
@timed(5.0)
def test_benchmark(self):
    pass # do something real slow and run nosetests --with-profile -a benchmark

Additional

On further investigation if you are using nosetests then it has you covered, see "http://somethingaboutorange.com/mrl/projects/nose/1.0.0/writing_tests.html".

You can have:

package level teardowns: (these live in package level init scripts)

def setup_package()
def teardown_package()

module level teardowns:

def setup_module()
def teardown_module()

class level:

class MyTestCase(unittest.TestCase):
    @classmethod
    def setup_class(cls): pass
    @classmethod
    def teardown_class(cls): pass

and test method level:

class MyTestCase(unittest.TestCase):
    def setUp(self): pass
    def tearDown(cls): pass

I like to use the the "setup_" names as it nicely delineates the nose specific entry points. I've verified these work nicely when run from nosetests via the command line. But they do NOT run from Pydev "run as unit test...". A potential solution may be writing a pydev plugin that uses nose to run the tests... perhaps someone has one? You could combine my hack with the nose ones calling common module functions to do the actual work. Ideally our init() would be aware of being launched from Pydev somehow.


Edit: Summary

Stepping through your test case with the debugger, it look's like this is a limitation of PyDev's test runner not supporting setUpClass(), at least not with 1.6.5, which I'm using.

Maybe this will be fixed in v2.0 of PyDev, but in the meantime, I think we will have to stick to using __init__() instead, as CarlS suggests.

Details

The PyDev 1.6.5 PyDevTestSuite class uses:

def run(self, result):
    for index, test in enumerate(self._tests):
        if result.shouldStop:
            break
        test(result)

        # Let the memory be released! 
        self._tests[index] = None

    return result

which is very similar to TestSuite.run() in python 2.6, whereas TestSuite.run() in python 2.7.1's unittest does rather more:

def run(self, result, debug=False):
    topLevel = False
    if getattr(result, '_testRunEntered', False) is False:
        result._testRunEntered = topLevel = True

    for test in self:
        if result.shouldStop:
            break

        if _isnotsuite(test):
            self._tearDownPreviousClass(test, result)
            self._handleModuleFixture(test, result)
            self._handleClassSetUp(test, result)
            result._previousTestClass = test.__class__

            if (getattr(test.__class__, '_classSetupFailed', False) or
                getattr(result, '_moduleSetUpFailed', False)):
                continue

        if not debug:
            test(result)
        else:
            test.debug()

    if topLevel:
        self._tearDownPreviousClass(None, result)
        self._handleModuleTearDown(result)
    return result

Old answer

I suspect this may be down to the version of Python that is being referenced.

If you check Window > Preferences > PyDev > Interpreter - Python and look at which Python Interpretter is being used, you may well find that it is pre v2.7 where, if I remember correctly, setUpClass was introduced.

Reference a newer version of python and I suspect your tests will work as is.


Instead of using unittest.main(), which automatically goes through all your test classes and individual tests, you can load the tests from each class separately and execute them at your discretion, including before each class test the necessary initializations. You can precede all that with some global initialization as well. See an example below:

import unittest

class MyTestClass1(unittest.TestCase):

    def setUp(self):
         pass

    @classmethod
    def setUpClass(cls):
        pass

    def test1(self):
         pass

    # lots of tests

class MyTestClass2(unittest.TestCase):

    def setUp(self):
         pass

    @classmethod
    def setUpClass(cls):
        pass

    def test1(self):
         pass

    # another whole lot of tests


if __name__=="__main__":
    # add your global initialization code here
    global_initialization()

    # MyTestClass1 initialization:
    MyTestClass1.setUpClass() # python 2.7 only

    suite = unittest.TestLoader().loadTestsFromTestCase(MyTestClass1)
    unittest.TextTestRunner().run(suite)

    # MyTestClass1 initialization:
    MyTestClass2.setUpClass() # python 2.7 only

    suite = unittest.TestLoader().loadTestsFromTestCase(MyTestClass2)
    unittest.TextTestRunner().run(suite)

The unittest documentation has some other examples of similar use which could be of assistance.

Edit: Unbeknown to me until this moment, it seems that Python 2.7 has a setUpClass(), (should be a class method, hence the decorator) which seems to do what you need it to . However, that's not the case in Python 2.6 and you should provide your own initialization routines.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜