Ensure tests get different names by adding an ordinal

This commit is contained in:
Carles Barrobés
2014-03-21 21:35:52 +01:00
parent 1b25f2a3bf
commit 5c00b6e844
4 changed files with 42 additions and 34 deletions

40
ddt.py
View File

@@ -4,7 +4,7 @@ import os
import re
from functools import wraps
__version__ = '0.7.1'
__version__ = '0.8.0'
# These attributes will not conflict with any real python attribute
# They are added to the decorated test method and processed later
@@ -62,18 +62,21 @@ def file_data(value):
return wrapper
def mk_test_name(name, value):
def mk_test_name(name, value, index=0):
"""
Generate a new name for the test named ``name``, appending ``value``.
Generate a new name for a test case.
It will take the original test name and append an ordinal index and a
string representation of the value, and convert the result into a valid
python identifier by replacing extraneous characters with ``_``.
"""
try:
test_name = "{0}_{1}".format(name, value)
value = str(value)
except UnicodeEncodeError:
# fallback for python2
test_name = "{0}_{1}".format(
name, value.encode('ascii', 'backslashreplace')
)
value = value.encode('ascii', 'backslashreplace')
test_name = "{0}_{1}_{2}".format(name, index + 1, value)
return re.sub('\W|^(?=\d)', '_', test_name)
@@ -87,9 +90,12 @@ def ddt(cls):
For each method decorated with ``@data``, this will effectively create as
many methods as data items are passed as parameters to ``@data``.
The names of the test methods follow the pattern ``test_func_name
+ "_" + str(data)``. If ``data.__name__`` exists, it is used
instead for the test method name.
The names of the test methods follow the pattern
``original_test_name_{ordinal}_{data}``. ``ordinal`` is the position of the
data argument, starting with 1.
For data we use a string representation of the data value converted into a
valid python identifier. If ``data.__name__`` exists, we use that instead.
For each method decorated with ``@file_data('test_data.json')``, the
decorator will try to load the test_data.json file located relative
@@ -97,11 +103,7 @@ def ddt(cls):
for each ``test_name`` key create as many methods in the list of values
from the ``data`` key.
The names of these test methods follow the pattern of
``test_name`` + str(data)``
"""
def feed_data(func, new_name, *args, **kwargs):
"""
This internal method decorator feeds the test data item to the test.
@@ -139,19 +141,19 @@ def ddt(cls):
add_test(test_name, _raise_ve, None)
else:
data = json.loads(open(data_file_path).read())
for elem in data:
for i, elem in enumerate(data):
if isinstance(data, dict):
key, value = elem, data[elem]
test_name = mk_test_name(name, key)
test_name = mk_test_name(name, key, i)
elif isinstance(data, list):
value = elem
test_name = mk_test_name(name, value)
test_name = mk_test_name(name, value, i)
add_test(test_name, func, value)
for name, func in list(cls.__dict__.items()):
if hasattr(func, DATA_ATTR):
for v in getattr(func, DATA_ATTR):
test_name = mk_test_name(name, getattr(v, "__name__", v))
for i, v in enumerate(getattr(func, DATA_ATTR)):
test_name = mk_test_name(name, getattr(v, "__name__", v), i)
if hasattr(func, UNPACK_ATTR):
if isinstance(v, tuple) or isinstance(v, list):
add_test(test_name, func, *v)

View File

@@ -38,3 +38,7 @@ And then run them with your favourite test runner, e.g. if you use nose::
The number of test cases actually run and reported separately has been
multiplied.
DDT will try to give the new test cases meaningful names by converting the
data values to valid python identifiers.

View File

@@ -130,7 +130,8 @@ def test_file_data_test_names_dict():
test_data_path = os.path.join(tests_dir, 'test_data_dict.json')
test_data = json.loads(open(test_data_path).read())
created_tests = set([
"test_something_again_{0}".format(name) for name in test_data.keys()
"test_something_again_{0}_{1}".format(index + 1, name)
for index, name in enumerate(test_data.keys())
])
assert_equal(tests, created_tests)
@@ -201,8 +202,8 @@ def test_ddt_data_name_attribute():
setattr(mytest, 'test_hello', data_hello)
ddt_mytest = ddt(mytest)
assert_is_not_none(getattr(ddt_mytest, 'test_hello_data1'))
assert_is_not_none(getattr(ddt_mytest, 'test_hello_2'))
assert_is_not_none(getattr(ddt_mytest, 'test_hello_1_data1'))
assert_is_not_none(getattr(ddt_mytest, 'test_hello_2_2'))
def test_ddt_data_unicode():
@@ -223,10 +224,9 @@ def test_ddt_data_unicode():
def test_hello(self, val):
pass
assert_is_not_none(getattr(mytest, 'test_hello_ascii'))
assert_is_not_none(getattr(mytest, 'test_hello_non_ascii__u2603'))
assert_is_not_none(
getattr(mytest, """test_hello__u__u2603____data__"""))
assert_is_not_none(getattr(mytest, 'test_hello_1_ascii'))
assert_is_not_none(getattr(mytest, 'test_hello_2_non_ascii__u2603'))
assert_is_not_none(getattr(mytest, 'test_hello_3__u__u2603____data__'))
elif six.PY3:
@@ -236,10 +236,9 @@ def test_ddt_data_unicode():
def test_hello(self, val):
pass
assert_is_not_none(getattr(mytest, 'test_hello_ascii'))
assert_is_not_none(getattr(mytest, 'test_hello_non_ascii__'))
assert_is_not_none(
getattr(mytest, """test_hello________data__"""))
assert_is_not_none(getattr(mytest, 'test_hello_1_ascii'))
assert_is_not_none(getattr(mytest, 'test_hello_2_non_ascii__'))
assert_is_not_none(getattr(mytest, 'test_hello_3________data__'))
def test_feed_data_with_invalid_identifier():
@@ -251,6 +250,8 @@ def test_feed_data_with_invalid_identifier():
obj = DummyInvalidIdentifier()
method = getattr(obj, tests[0])
assert_equal(method.__name__,
'test_data_with_invalid_identifier_32v2_g__Gmw845h_W_b53wi_')
assert_equal(
method.__name__,
'test_data_with_invalid_identifier_1_32v2_g__Gmw845h_W_b53wi_'
)
assert_equal(method(), '32v2 g #Gmw845h$W b53wi.')

View File

@@ -9,14 +9,15 @@ deps =
flake8
six
commands =
nosetests --with-coverage --cover-package=ddt --cover-html
nosetests -s --with-coverage --cover-package=ddt --cover-html
flake8 ddt.py test
[testenv:py27]
deps =
{[testenv]deps}
Sphinx
sphinxcontrib-programoutput
commands =
nosetests --with-coverage --cover-package=ddt --cover-html
nosetests -s --with-coverage --cover-package=ddt --cover-html
flake8 ddt.py test
sphinx-build -b html docs docs/_build