Skip to content

Implemented mock support #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 72 additions & 2 deletions ddt.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import re
from functools import wraps

import mock

try:
import yaml
except ImportError: # pragma: no cover
Expand All @@ -27,6 +29,7 @@
DATA_ATTR = '%values' # store the data the test must run with
FILE_ATTR = '%file_path' # store the path to JSON file
UNPACK_ATTR = '%unpack' # remember that we have to unpack values
MOCK_ATTR = '%mockdata' # store mock oriented data


try:
Expand All @@ -52,6 +55,15 @@ def unpack(func):
return func


def mockdata(func):
"""
Method decorator to add mock oriented functionality.

"""
setattr(func, MOCK_ATTR, True)
return func


def data(*values):
"""
Method decorator to add to your test methods.
Expand Down Expand Up @@ -222,6 +234,46 @@ def _add_tests_from_data(cls, name, func, data):
add_test(cls, test_name, func, value)


def mockdata_multiple_args(v):
"""
Convert mock `MagicMock` object from mock `patch` from passed arguments.
"""
values = ()

for el in list(v):
if isinstance(el, mock.mock._patch):
mock_obj = el.start()
values += (mock_obj,)
else:
values += (el,)

return values


def mockdata_dict(v):
"""
Set user defined attributes as dict keys to mock `MagicMock` object.
"""
allowed_attributes = ['return_value', 'side_effect']

with v['patch'] as mock_object:
for attr in allowed_attributes:
defined_attr = v.get(attr, 'User did not define mock attribute.')

if defined_attr is not 'User did not define mock attribute.':
setattr(mock_object, attr, defined_attr)

return (mock_object,)


def mockdata_single_arg(arg):
"""
Return mock `MagicMock` object if passed argument is a mock `patch`.
"""
if isinstance(arg, mock.mock._patch):
return arg.start()


def ddt(cls):
"""
Class decorator for subclasses of ``unittest.TestCase``.
Expand Down Expand Up @@ -251,13 +303,31 @@ def ddt(cls):
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):

if hasattr(func, MOCK_ATTR):
# mock as multiple args
v = mockdata_multiple_args(v)

add_test(cls, test_name, func, *v)
else:
# unpack dictionary
add_test(cls, test_name, func, **v)

if hasattr(func, MOCK_ATTR):
# mock with attr as dict key
v = mockdata_dict(v)
add_test(cls, test_name, func, *v)

else:
# unpack dictionary
add_test(cls, test_name, func, **v)
else:
if hasattr(func, MOCK_ATTR):
# mock as single argument
v = mockdata_single_arg(v)

add_test(cls, test_name, func, v)

delattr(cls, name)
elif hasattr(func, FILE_ATTR):
file_attr = getattr(func, FILE_ATTR)
Expand Down
98 changes: 97 additions & 1 deletion test/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
import six
import mock

from ddt import ddt, data, file_data
from ddt import (
ddt, data, file_data, unpack,
mockdata, MOCK_ATTR,
mockdata_multiple_args, mockdata_dict, mockdata_single_arg
)
from nose.tools import (
assert_true, assert_equal, assert_is_not_none, assert_raises
)
Expand Down Expand Up @@ -70,6 +74,22 @@ def test_something_again(self, value):
return value


@ddt
class MockDataDummy(object):
"""
Dummy class to test the mockdata decorator on
"""

@data([mock.patch('ddt.ddt'), 5, AttributeError()])
@unpack
@mockdata
def test_something(self, mock_obj, return_value, side_effect):
mock_obj.return_value = return_value
mock_obj.side_effect = side_effect

return mock_obj


def test_data_decorator():
"""
Test the ``data`` method decorator
Expand Down Expand Up @@ -320,3 +340,79 @@ def test_file_data_yaml_dict(self, value):
for test in tests:
method = getattr(obj, test)
assert_raises(ValueError, method)


def test_feed_data_mockdata():
"""
Test that data is fed to the decorated tests
"""
tests = list(filter(_is_test, MockDataDummy.__dict__))
assert_equal(len(tests), 1)

obj = MockDataDummy()
method_result = getattr(obj, tests[0])()

assert_true(isinstance(type(method_result), type(mock.MagicMock)))
assert_equal(5, method_result.return_value)

assert_equal(
AttributeError().__class__, method_result.side_effect.__class__
)


def test_mockdata_decorator():
"""
Test that func decorated by `mockdata` has `MOCK_ATTR` (`%mockdata`).
"""

def hello():
pass

mockdata(hello)

assert_true(hello.__getattribute__(MOCK_ATTR))


def test_mockdata_single_arg():
"""
Test returning mock `MagicMock` object if passed argument is a mock `patch`
"""
arg = mock.patch('ddt.ddt')
result = mockdata_single_arg(arg)
assert_true(isinstance(type(result), type(mock.MagicMock)))

arg = 3
result = mockdata_single_arg(arg)
assert_equal(None, result)


def test_mockdata_dict():
"""
Test that user defined attributes are set to mock `MagicMock` object.
"""
moke_attr = {
'patch': mock.patch('ddt.ddt'),
'return_value': 5,
'side_effect': AttributeError()
}

result = mockdata_dict(moke_attr)
mock_obj = result[0] # result is tuple

assert_true(isinstance(type(mock_obj), type(mock.MagicMock)))
assert_equal(5, mock_obj.return_value)
assert_equal(AttributeError().__class__, mock_obj.side_effect.__class__)


def test_mockdata_multiple_args():
"""
Test that mock `patch` args are converted to mock `MagicMock` objects.
"""
values = [mock.patch('ddt.ddt'), mock.patch('ddt.unpack'), 5, 'Mock']

result = list(mockdata_multiple_args(values))

assert_true(isinstance(type(result[0]), type(mock.MagicMock)))
assert_true(isinstance(type(result[1]), type(mock.MagicMock)))
assert_equal(values[2], result[2])
assert_equal(values[3], result[3])