Source code for taurus.test.base

#!/usr/bin/env python

#############################################################################
##
## This file is part of Taurus
## 
## http://taurus-scada.org
##
## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
## 
## Taurus is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
## 
## Taurus is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU Lesser General Public License for more details.
## 
## You should have received a copy of the GNU Lesser General Public License
## along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
##
#############################################################################

"""This module provides basic utilities for usage in any test"""

__docformat__ = 'restructuredtext'

import functools


def insertTest(klass=None, helper_name=None, test_method_name=None,
               test_method_doc=None, tested_name=None, **helper_kwargs):
    """Decorator that inserts test methods from a helper method that accepts
    arguments.
    `insertTest` provides a very economic API for creating new tests for a given
    class based on a helper method.
    `insertTest` accepts the following arguments:
    
      - helper_name (str): the name of the helper method. `insertTest` will
                           insert a test method which calls the helper with
                           any the helper_kwargs (see below).
      - test_method_name (str): Optional. Name of the test method to be used.
                                If None given, one will be generated from the
                                tested class and helper names.
      - test_method_doc (str): Optional. The docstring for the inserted test 
                               method (this shows in the unit test output). 
                               If None given, a default one is generated which
                               includes the input parameters and the helper
                               name.
      - tested_name (str): Optional. The name of the class or feature being 
                           tested (if given, it will be used in default method 
                           names and docstrings).
      - \*\*helper_kwargs: All remaining keyword arguments are passed to the
                           helper.
    
    This decorator can be considered a "base" decorator. It is often used to
    create other decorators in which the helper method is pre-set, as in 
    the following example::
        
        isPos = functools.partial(insertTest, helper_name='isPositive')
        
        @isPos(x=2)
        @isPos(x=10)
        class Foo(unittest.TestCase):
            def isPositive(self, x):
                self.assertTrue(x > 0)
    
    """
    # Recipe to support decorating with and without arguments
    if klass is None: 
        return functools.partial(insertTest, helper_name=helper_name,
                                 test_method_name=test_method_name,
                                 test_method_doc=test_method_doc,
                                 tested_name=tested_name, **helper_kwargs)
    
    # Check arguments and provide defaults
    if helper_name is None:
        raise ValueError('helper_name argument is not optional')
    
    if test_method_name is None:
        test_method_name = 'test_'
        if tested_name:
            test_method_name += '%s_' % tested_name
        test_method_name += helper_name
    # Append an index if necessary to avoid overwriting existing test methods
    name, i = test_method_name, 1
    while (hasattr(klass, name)):
        i += 1
        name = "%s_%i" % (test_method_name, i)
    test_method_name = name
    
    if test_method_doc is None:
        argsrep = ', '.join(['%s=%s' % (k, repr(v))
                             for k, v in helper_kwargs.items()])
        if tested_name:
            test_method_doc = 'Testing %s with %s(%s)' % (tested_name,
                                                          helper_name, argsrep)
        else:
            test_method_doc = 'Testing %s(%s)' % (helper_name, argsrep)
    
    # New test implementation
    def newTest(obj):
        helper = getattr(obj, helper_name)
        return helper(**helper_kwargs)
    
    # Add the custom docstring
    newTest.__doc__ = test_method_doc
    
    # Add the new test method with the new implementation
    setattr(klass, test_method_name, newTest)
    
    return klass
    

if __name__ == '__main__':
    
    # a demo of use of insertTest

    from taurus.external import unittest

    isPos = functools.partial(insertTest, helper_name='isPositive')
    isNeg = functools.partial(insertTest, helper_name='isPositive', 
                              expected=False)

    @isPos
    @isPos(x=2)
    @isPos(x=10)
    @isPos(x=5)
    @isNeg(x=-1)
    class FooTest(unittest.TestCase):
        def isPositive(self, x=1, expected=True):
            self.assertEqual(x > 0, expected)
   
    unittest.main(verbosity=2)