153 lines
5.0 KiB
Python
153 lines
5.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Adds Parameterized tests for Python's unittest module
|
|
#
|
|
# Code from: parameterizedtestcase, version: 0.1.0
|
|
# Homepage: https://github.com/msabramo/python_unittest_parameterized_test_case
|
|
# Author: Marc Abramowitz, email: marc@marc-abramowitz.com
|
|
# License: MIT
|
|
#
|
|
# Fixed for to work in Python 2 & 3 with "add_metaclass" decorator from six
|
|
# https://pypi.python.org/pypi/six
|
|
# Author: Benjamin Peterson
|
|
# License: MIT
|
|
#
|
|
# Use like this:
|
|
#
|
|
# from parameterizedtestcase import ParameterizedTestCase
|
|
#
|
|
# class MyTests(ParameterizedTestCase):
|
|
# @ParameterizedTestCase.parameterize(
|
|
# ("input", "expected_output"),
|
|
# [
|
|
# ("2+4", 6),
|
|
# ("3+5", 8),
|
|
# ("6*9", 54),
|
|
# ]
|
|
# )
|
|
# def test_eval(self, input, expected_output):
|
|
# self.assertEqual(eval(input), expected_output)
|
|
|
|
try:
|
|
import unittest2 as unittest
|
|
except ImportError: # pragma: no cover
|
|
import unittest
|
|
|
|
from functools import wraps
|
|
import collections
|
|
|
|
|
|
def add_metaclass(metaclass):
|
|
"""Class decorator for creating a class with a metaclass."""
|
|
def wrapper(cls):
|
|
orig_vars = cls.__dict__.copy()
|
|
orig_vars.pop('__dict__', None)
|
|
orig_vars.pop('__weakref__', None)
|
|
slots = orig_vars.get('__slots__')
|
|
if slots is not None:
|
|
if isinstance(slots, str):
|
|
slots = [slots]
|
|
for slots_var in slots:
|
|
orig_vars.pop(slots_var)
|
|
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
|
return wrapper
|
|
|
|
|
|
def augment_method_docstring(method, new_class_dict, classname,
|
|
param_names, param_values, new_method):
|
|
param_assignments_str = '; '.join(
|
|
['%s = %s' % (k, v) for (k, v) in zip(param_names, param_values)])
|
|
extra_doc = "%s (%s.%s) [with %s] " % (
|
|
method.__name__, new_class_dict.get('__module__', '<module>'),
|
|
classname, param_assignments_str)
|
|
|
|
try:
|
|
new_method.__doc__ = extra_doc + new_method.__doc__
|
|
except TypeError: # Catches when new_method.__doc__ is None
|
|
new_method.__doc__ = extra_doc
|
|
|
|
|
|
class ParameterizedTestCaseMetaClass(type):
|
|
method_counter = {}
|
|
|
|
def __new__(meta, classname, bases, class_dict):
|
|
new_class_dict = {}
|
|
|
|
for attr_name, attr_value in list(class_dict.items()):
|
|
if isinstance(attr_value, collections.Callable) and hasattr(attr_value, 'param_names'):
|
|
# print("Processing attr_name = %r; attr_value = %r" % (
|
|
# attr_name, attr_value))
|
|
|
|
method = attr_value
|
|
param_names = attr_value.param_names
|
|
data = attr_value.data
|
|
func_name_format = attr_value.func_name_format
|
|
|
|
meta.process_method(
|
|
classname, method, param_names, data, new_class_dict,
|
|
func_name_format)
|
|
else:
|
|
new_class_dict[attr_name] = attr_value
|
|
|
|
return type.__new__(meta, classname, bases, new_class_dict)
|
|
|
|
@classmethod
|
|
def process_method(
|
|
cls, classname, method, param_names, data, new_class_dict,
|
|
func_name_format):
|
|
method_counter = cls.method_counter
|
|
|
|
for param_values in data:
|
|
new_method = cls.new_method(method, param_values)
|
|
method_counter[method.__name__] = \
|
|
method_counter.get(method.__name__, 0) + 1
|
|
case_data = dict(list(zip(param_names, param_values)))
|
|
case_data['func_name'] = method.__name__
|
|
case_data['case_num'] = method_counter[method.__name__]
|
|
|
|
new_method.__name__ = func_name_format.format(**case_data)
|
|
|
|
augment_method_docstring(
|
|
method, new_class_dict, classname,
|
|
param_names, param_values, new_method)
|
|
new_class_dict[new_method.__name__] = new_method
|
|
|
|
@classmethod
|
|
def new_method(cls, method, param_values):
|
|
@wraps(method)
|
|
def new_method(self):
|
|
return method(self, *param_values)
|
|
|
|
return new_method
|
|
|
|
@add_metaclass(ParameterizedTestCaseMetaClass)
|
|
class ParameterizedTestMixin(object):
|
|
@classmethod
|
|
def parameterize(cls, param_names, data,
|
|
func_name_format='{func_name}_{case_num:05d}'):
|
|
"""Decorator for parameterizing a test method - example:
|
|
|
|
@ParameterizedTestCase.parameterize(
|
|
("isbn", "expected_title"), [
|
|
("0262033844", "Introduction to Algorithms"),
|
|
("0321558146", "Campbell Essential Biology")])
|
|
|
|
"""
|
|
|
|
def decorator(func):
|
|
@wraps(func)
|
|
def newfunc(*arg, **kwargs):
|
|
return func(*arg, **kwargs)
|
|
|
|
newfunc.param_names = param_names
|
|
newfunc.data = data
|
|
newfunc.func_name_format = func_name_format
|
|
|
|
return newfunc
|
|
|
|
return decorator
|
|
|
|
@add_metaclass(ParameterizedTestCaseMetaClass)
|
|
class ParameterizedTestCase(unittest.TestCase, ParameterizedTestMixin):
|
|
pass
|