Ensure tests get different names by adding an ordinal
This commit is contained in:
40
ddt.py
40
ddt.py
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.')
|
||||
|
||||
5
tox.ini
5
tox.ini
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user