I am using a Python Mock object for a third-party package that needs to JSON serialize my mock. This means that I cannot change the invocation of json.dumps
, so must use the solution here: https://stackoverflow.com/a/31207881/19643198
class FileItem(dict):
def __init__(self, fname):
dict.__init__(self, fname=fname)
f = FileItem('tasks.txt')
json.dumps(f) #No need to change anything here
The only problem is that my object is not of class FileItem, but needs to be a MagicMock. This suggests multiple inheritance, so something like:
class FileItem(MagicMock, dict):
def __init__(self):
MagicMock.__init__(self)
dict.__init__(self)
Unfortunately, multiple inheritance from both dict and MagicMock seems not to work. In case this helps make this problem easier to solve, the third-party library does not need to deserialize or even use the JSON serialized representation of the MagicMock.
There is no need to consider multiple inheritance. The problem is that Python's JSON module doesn't know how to serialize certain types, like MagicMock
(or other common types like datetime
, for that matter).
You can tell json
how to deal with unknown types by either using the cls=
or default=
parameters, but since you are asking about unit tests and don't want to modify the code you're testing, you can solve this by also mocking json.dumps
(or json.dump
).
For instance,
import json
from json import dumps as _dumps
from unittest.mock import MagicMock, Mock
f1 = {'fname': 'tasks.txt'} # a normal dict
f2 = {'fname': MagicMock()} # some value containing a MagicMock instance
def dumps_wrapper(*args, **kwargs):
return _dumps(*args, **(kwargs | {"default": lambda obj: "mock"}))
# mock the `dumps` function
json.dumps = MagicMock(wraps=dumps_wrapper)
# now you can serialize objects containing MagicMock (or Mock) objects
json.dumps(f1) # {"fname": "tasks.txt"}
json.dumps(f2) # {"fname": "mock"}
In your unit tests, you can use the patch
function to temporarily patch the json.dumps
function:
# my_module.py
import json
def make_json(obj):
return json.dumps(obj)
# test_mocked_json.py
import unittest
from unittest.mock import MagicMock, patch
from json import dumps as _dumps
import my_module
def dumps_wrapper(*args, **kwargs):
return _dumps(*args, **(kwargs | {"default": lambda obj: "mock"}))
class TestMockedJSON(unittest.TestCase):
def test_mocked_json(self):
patch_json = patch(
"my_module.json.dumps",
MagicMock(wraps=dumps_wrapper))
with patch_json:
f1 = {'fname': 'tasks.txt'} # a normal dict
f2 = {'fname': MagicMock()} # some value containing a MagicMock instance
s1 = my_module.make_json(f1)
s2 = my_module.make_json(f2)
assert s1
assert s2
print(s1, s2, "ok!")
Which you can run with:
python -m unittest test_mocked_json.py
# {"fname": "tasks.txt"} {"fname": "mock"} ok!
# .
# ----------------------------------------------------------------------
# Ran 1 test in 0.001s
#
# OK