Issue
I wrote a decorator that halts my program with breakpoint() if a critical error happens.
def the_breakpoint_dec(func):
@functools.wraps(func):
async def my_wrapper(func):
retval=await func(*args,**kwargs)
return retval if retval else breakpoint()
return my_wrapper
I'm doing unit testing and want to make sure the breakpoint hits, without actually triggering it, so I've been trying to patch breakpoint() with the @mock.patch decorator, but it's not working. Some of the tested paths to breakpoint of the 20 or so i've tried are
@mock.patch('pdb.breakpoint')
@mock.patch('builtins.breakpoint')
@mock.patch('builtins.pdb.breakpoint')
@mock.patch('my_module.breakpoint')
@mock.patch('my_module.builtins.breakpoint')
@mock.patch('sys.breakpointhook')
@mock.patch('my_module.sys.breakpointhook')
@mock.patch('my_decorator_module.sys.breakpointhook')
@mock.patch('my_decorator_module.sys.breakpoint')
I then make the test function def and pass in the mock:
#testmodule.py
pytestmark=pytest.mark.asyncio
@mock.patch('just_another_path_to_breakpoint()_that_doesnt_work')
async def test_func_to_test(self, mock_breakpoint, some_other_mock):
mock_breakpoint.return_value=None
await decorated_func()
mock_breakpoint.assert_called() # never hit because pdb prompt opens in interpreter
module under test
#moduleundertest.py
class bar:
@the_breakpoint_dec
async def decorated_func(self):
for i in range(3)
try:
return await something_likely_to_fail()
except:
pass
Initially the breakpoint() I wanted to patch was being called from the mentioned decorator. After several hours of failures I commented out the decorator and moved breakpoint() directly into the method under test. The the patching still failed. What is the proper way to patch this builtin, and if some special thing must be done to patch it when it's in a decorator, what thing is that?
Solution
To solve, upgrade your project to 3.8!
I tried to reproduce your scenario in 3.8 and couldn't, the patching worked fine for me. But then when trying with 3.7 and below, the patching stopped working and my program would enter the breakpoint. I have no explanation for this.
Here's a complete running example I coded up. It runs the function_to_test
, simulating success and then failure (True
/False
). As proven by the assert
s it only invokes breakpoint()
in the failure case, but the test exits cleanly due to our mocked breakpoint()
-- at least in 3.8.
from unittest import mock
import functools
import asyncio
def break_if_function_fails(func):
@functools.wraps(func)
async def my_wrapper(*args, **kwargs):
retval = await func(*args, **kwargs)
return retval if retval else breakpoint()
return my_wrapper
@break_if_function_fails
async def function_to_test(retval):
return retval
@mock.patch('builtins.breakpoint')
async def do_test(mock_breakpoint):
print('Entering test')
await function_to_test(True)
mock_breakpoint.assert_not_called()
await function_to_test(False)
mock_breakpoint.assert_called()
print('Exiting test')
asyncio.run(do_test())
The exact same code does not finish but triggers the breakpoint in 3.7.
If you must stay with 3.7, you can still do
@mock.patch('builtins.breakpoint')
async def do_test(mock_breakpoint):
backup_breakpoint = builtins.breakpoint
builtins.breakpoint = mock_breakpoint
...
builtins.breakpoint = backup_breakpoint
but this is obviously ugly.
Answered By - xjcl
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.