Noam Manos
Noam Manos

Reputation: 16990

How to change docstring of TestCase Class in Python?

In Python 2.5 (Jython actually), for the UnitTest TestCase Class - there's is no SetUpClass method, and __init__ is not really acceptable (no refference to self). When I try to change docstring inside the TestCase:

import os
fileName = os.path.split(__file__)[1]
testCaseName = os.path.splitext(fileName)[0]
setattr(__name__, '__doc__', testCaseName)

I'm getting:

setattr(__name__, '__doc__', testCaseName)
TypeError: readonly attribute

I tried to change the docstring by instantiate it into an object (where self.__doc__ is writable).

UPDATED: but I want to avoid additional coding in the sub-class (i.e. inheriting super-class function to set docstring of sub-class), for example:

File DynamicTestCase.py includes:

class DynamicTestCase(unittest.TestCase):
    def setDocstring(self, testCaseDocstring=None):
        if not testCaseDocstring:
            fileName = os.path.split(__file__)[1]
            testCaseDocstring = os.path.splitext(fileName)[0]
        setattr(self, '__doc__', testCaseDocstring)

File MyTestCase.py includes:

class MyTestCase(DynamicTestCase):
    def test_print_docstring(self):
        self.setDocstring()
        print 'MyTestCase Docstring = ', self.__doc__

But still, the unittest run result is:

MyTestCase Docstring = DynamicTestCase

When I expected MyTestCase Docstring = MyTestCase

Upvotes: 1

Views: 1428

Answers (2)

samplebias
samplebias

Reputation: 37919

Updated - __file__ is the path name from which the current module was loaded, so naturally using __file__ inside DynamicTestCase.py will result in the path DynamicTestCase.py. However, you can just pass the path into setDocstring() from subclasses like this:

DynamicTestCase.py:

class DynamicTestCase(unittest.TestCase):
    def setDocstring(self, docstring=None):
        if docstring is None:
            docstring = __file__
        if os.path.exists(docstring):
            name = os.path.split(docstring)[1]
            docstring = os.path.splitext(name)[0]
        setattr(self, '__doc__', docstring)

MyTestCase.py:

class MyTestCase(DynamicTestCase):
    def __init__(self, *args, **kwargs):
        DynamicTestCase.__init__(self, *args, **kwargs)
        self.setDocstring(__file__)

    def test_print_docstring(self):
        print 'MyTestCase Docstring = ', self.__doc__

    def test_new_docstring(self):
        self.setDocstring('hello')
        print 'MyTestCase Docstring = ', self.__doc__

Output:

MyTestCase Docstring =  MyTestCase
MyTestCase Docstring =  hello

Rest of answer

In your original code above __name__ is a string, not a class. Jython rightly rejects altering the __doc__ attribute on the str type.

Could you explain a bit about why you want to change TestCase's docstring? For example, you could subclass TestCase and give your own docstring:

class MyTestCase(unittest.TestCase):
    "Docstring of MyTestCase"

Not sure if you've tried it yet, but the unittest2 package's TestCase has setUpClass, tearDownClass class methods. It's a backport of Python 2.7's improvements to work with Python 2.6 and prior.

Jython allows you to set the __doc__ of new-style classes, but CPython does not. For that reason you might want to find another way to accomplish your goal if you want your code to be portable:

Jython 2.2.1 on java1.6.0_24
>>> unittest.TestCase.__doc__ = 'foo bar'
>>> unittest.TestCase.__doc__
'foo bar'

Python 2.6.6 (r266:84292, Feb 12 2011, 01:07:21)
>>> unittest.TestCase.__doc__ = 'foo bar'
AttributeError: attribute '__doc__' of 'type' objects is not writable

Upvotes: 1

Joseph Lisee
Joseph Lisee

Reputation: 3511

You are grabbing the filename of the DynamicTestCase file, not the file that is calling the function. In order to get that you have to go into it's stack frame:

  import inspect

  class DynamicTestCase(unittest.TestCase):
    def setDocstring(self, testCaseDocstring=None):
        if not testCaseDocstring:
            fileName = 'unknown.py'

            # Go up one stack frame and grab the file name
            stack = inspect.stack()
            try:
                frame = stack[1][0]
                fileName = frame.f_code.co_filename        
            finally:
                del stack

            testCaseDocstring = os.path.splitext(fileName)[0]

Upvotes: 0

Related Questions