It works fine with pytest 6.0.2 or with the older versions, it doesn't work with both 6.1.0 and 6.1.1.
It just crashes preventing the final report with failing tests details.
$ python
Python 3.8.6 (default, Sep 25 2020, 09:36:53)
[GCC 10.2.0] on linux
$ uname -a
Linux debian 5.8.0-2-amd64 #1 SMP Debian 5.8.10-1 (2020-09-19) x86_64 GNU/Linux
$ pip list
Package Version Location
----------------------------- ---------- --------------------------------------------------------
channels 2.4.0
channels-redis 3.1.0
Django 3.1.2
djangorestframework 3.12.1
djangorestframework-simplejwt 4.4.0
pytest 6.1.0
pytest-asyncio 0.14.0
pytest-cov 2.10.1
pytest-django 3.10.0
pytest-mock 3.3.1
pytest-ordering 0.6
selenium 3.141.0
md5-3b1769e423b7ed4ea1200f21b7204d95
utils/tests/test_helpers.py::TestUserMediaImagesHelpersFunctions::test_utils_helpers_reduced_image_size_returns_max_height PASSED [ 95%]
utils/tests/test_helpers.py::TestUserMediaImagesHelpersFunctions::test_utils_helpers_save_user_image_calls_reduced_image_size_thumbnail
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/main.py", line 257, in wrap_session
INTERNALERROR> session.exitstatus = doit(config, session) or 0
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/main.py", line 313, in _main
INTERNALERROR> config.hook.pytest_runtestloop(session=session)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR> return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR> self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR> return outcome.get_result()
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR> raise ex[1].with_traceback(ex[2])
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/main.py", line 338, in pytest_runtestloop
INTERNALERROR> item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR> return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR> self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR> return outcome.get_result()
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR> raise ex[1].with_traceback(ex[2])
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/runner.py", line 110, in pytest_runtest_protocol
INTERNALERROR> runtestprotocol(item, nextitem=nextitem)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/runner.py", line 127, in runtestprotocol
INTERNALERROR> reports.append(call_and_report(item, "call", log))
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/runner.py", line 220, in call_and_report
INTERNALERROR> hook.pytest_runtest_logreport(report=report)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR> return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR> self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR> return outcome.get_result()
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR> raise ex[1].with_traceback(ex[2])
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/terminal.py", line 546, in pytest_runtest_logreport
INTERNALERROR> self.write_ensure_prefix(line, word, **markup)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/terminal.py", line 408, in write_ensure_prefix
INTERNALERROR> self._tw.write(prefix)
INTERNALERROR> File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/_io/terminalwriter.py", line 155, in write
INTERNALERROR> self._file.write(msg)
INTERNALERROR> TypeError: write() argument must be str, not MagicMock
Destroying test database for alias 'default'...
If I comment out that last test (test_utils_helpers_save_user_image_calls_reduced_image_size_thumbnail) then it fails on the very previous one (test_utils_helpers_reduced_image_size_returns_max_height).
There is some sort of active mock that interferes with pytest internals. I suggest temporarily adding a print() or breakpoint() call before
"/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/_io/terminalwriter.py", line 155
to find out which mock it is, then we will have more information.
Well, thx, it looks like I've managed to isolate the error after adding the following block of code where you suggested:
except TypeError:
breakpoint()
The problem is with mocking module's os.path.join (this syntax is functionality from pytest-mock package):
def test_utils_some_function_returns_empty_list_for_not_existing_path(self, mocker):
mocker.patch("utils.helpers.os.path.join")
mocker.patch("utils.helpers.os.path.exists", return_value=False)
assert some_function(mocker.MagicMock()) == []
Wherever I use that os.path.join patch it will crash the test runner. The main problem is that this worked prior to 6.1.0 version.
Some internal details in pytest changed, but it is not quite possible for pytest to protect itself against all sorts of patching of functions it uses. In this particular case I recommend changing the test to clean up the mock on its own instead of letting pytest do it:
with mocker.patch("utils.helpers.os.path.join"), mocker.patch("utils.helpers.os.path.exists", return_value=False):
assert some_function(mocker.MagicMock()) == []
I'm afraid that's not possible in pytest-mock, it raises "ValueError: Using mocker in a with context is not supported.":
https://github.com/pytest-dev/pytest-mock#note-about-usage-as-context-manager
Although mocker's API is intentionally the same as mock.patch's, its use as context manager and function decorator is not supported through the fixture:
def test_context_manager(mocker):
a = A()
with mocker.patch.object(a, 'doIt', return_value=True, autospec=True): # DO NOT DO THIS
assert a.doIt() == True
The purpose of this plugin is to make the use of context managers and function decorators for mocking unnecessary.
Oops, didn't know. So in this case I'd use unittest.mock.patch directly. Or better, try to avoid patching functions like os.path.join as it is very widely used. To make it a little safer you can create a wrapper function over os.path.join in your module and then patch that.
Yep, it works with unittest.mock.patch.
Thank you very much for your time and effort!
I'm glad we worked out what was happening :slightly_smiling_face:
Most helpful comment
Oops, didn't know. So in this case I'd use
unittest.mock.patchdirectly. Or better, try to avoid patching functions likeos.path.joinas it is very widely used. To make it a little safer you can create a wrapper function overos.path.joinin your module and then patch that.