Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deriving a class from TestCase throws two errors

I have some basic setup/teardown code that I want to reuse in a whole bunch of unit tests. So I got the bright idea of creating some derived classes to avoid repeating code in every test class.

In so doing, I received two strange errors. One, I cannot solve. Here is the unsolvable one:

AttributeError: 'TestDesktopRootController' object has no attribute '_testMethodName' 

Here is my base class:

import unittest import twill import cherrypy from cherrypy._cpwsgi import CPWSGIApp   class BaseControllerTest(unittest.TestCase):      def __init__(self):         self.controller = None      def setUp(self):         app = cherrypy.Application(self.controller)          wsgi = CPWSGIApp(app)          twill.add_wsgi_intercept('localhost', 8080, lambda : wsgi)      def tearDown(self):         twill.remove_wsgi_intercept('localhost', 8080) 

And here is my derived class:

import twill from base_controller_test import BaseControllerTest  class TestMyController(BaseControllerTest):      def __init__(self, args):         self.controller = MyController()         BaseControllerTest.__init__(self)      def test_root(self):         script = "find 'Contacts'"         twill.execute_string(script, initial_url='http://localhost:8080/') 

The other strange error is:

TypeError: __init__() takes exactly 1 argument (2 given) 

The "solution" to that was to add the word "args" to my __init__ function in the derived class. Is there any way to avoid that?

Remember, I have two errors in this one.

like image 832
101010 Avatar asked Sep 27 '11 00:09

101010


People also ask

How do you check if a function raises an error in Python?

There are two ways you can use assertRaises: using keyword arguments. Just pass the exception, the callable function and the parameters of the callable function as keyword arguments that will elicit the exception. Make a function call that should raise the exception with a context.

How do you handle exceptions in unit test Python?

assertRaises(exception, callable, *args, **kwds) Test that an exception (first argument) is raised when a function is called with any positional or keyword arguments. The test passes if the expected exception is raised, is an error if another exception is raised, or fails if no exception is raised.

How do you test try and except in Python?

The try block lets you test a block of code for errors. The except block lets you handle the error. The else block lets you execute code when there is no error. The finally block lets you execute code, regardless of the result of the try- and except blocks.

What is the purpose of using self ID in tests?

b) self.id returns the name of method The test object is represented by the string returned. The string returned consists of the full name of the test method, It's module name, it's class name.


2 Answers

It's because you're overriding __init__() incorrectly. Almost certainly, you don't want to override __init__() at all; you should do everything in setUp(). I've been using unittest for >10 years and I don't think I've ever overridden __init__().

However, if you really do need to override __init__(), remember that you don't control where your constructor is called -- the framework calls it for you. So you have to provide a signature that it can call. From the source code (unittest/case.py), that signature is:

def __init__(self, methodName='runTest'): 

The safe way to do this is to accept any arguments and just pass 'em up to the base class. Here is a working implementation:

class BaseTest(unittest.TestCase):     def __init__(self, *args, **kwargs):         unittest.TestCase.__init__(self, *args, **kwargs)      def setUp(self):         print "Base.setUp()"      def tearDown(self):         print "Base.tearDown()"   class TestSomething(BaseTest):     def __init__(self, *args, **kwargs):         BaseTest.__init__(self, *args, **kwargs)         self.controller = object()      def test_silly(self):         self.assertTrue(1+1 == 2) 
like image 140
Greg Ward Avatar answered Sep 24 '22 13:09

Greg Ward


In BaseController's __init__ you need to call unittest.TestCase's __init__ just like you did in TestMyController.

The call to construct a TestCase from the framework may be passing an argument. The best way to handle this for deriving classes is:

class my_subclass(parentclass):     def __init__(self, *args, **kw):         parentclass.__init__(self, *args, **kw)         ... 
like image 36
KQ. Avatar answered Sep 24 '22 13:09

KQ.