I have a multiprocessing application that has workers running in "while True" loops. For testing, I'd like to be able to mock sys.exit() in a way like this:
with mock.patch('sys.exit') as sys_mock:
sys_mock.side_effect = break
or
with mock.patch('sys.exit') as sys_mock:
sys_mock.side_effect = return
So I can break out of the loop and complete my test. Neither of these work, but is there a different way to do what I'm trying to accomplish?
You can use an Exception
as side_effect
to simulate sys.exit
behavior without exit from your test.
side_effect
documentation say:
This can either be a function to be called when the mock is called, an iterable or an exception (class or instance) to be raised.
So you can not use a statement like break
or return
but what you want to do is exit from your run cycle and that can be obtained by raise an Exception ... I hope you don't use a wild try
-except
in your thread's main cycle.
I wrote a simple example to test it, I used decorator patch
syntax and put inline side_effect=Exception
that make the test more readable:
import sys
import threading
import unittest
from unittest.mock import patch
class T(threading.Thread):
def __init__(self, *args, **kwargs):
super(T, self).__init__(*args, **kwargs)
self._interrupt = threading.Event()
self.started = threading.Event() #Used to be sure that we test run() behavior
self.started.clear()
self.terminated = False
def interrupt(self):
self._interrupt.set()
def run(self, *args, **kwargs):
self._interrupt.clear()
self.started.set()
while not self._interrupt.is_set():
self._interrupt.wait(timeout=1)
self.terminated = True
sys.exit()
class TestInterrupt(unittest.TestCase):
@patch("sys.exit", side_effect=Exception("Ignore it... just close thread"))
def test_interrupt(self, mock_sys_exit):
t = T()
t.start()
if not t.started.is_set():
t.started.wait(timeout=0.2)
self.assertTrue(t.started.is_set(), "t not started!")
#Ok t is in run() main cycle: we can test interrupt
t.interrupt()
t.join(0.1)
self.assertTrue(t.terminated)
self.assertFalse(t.isAlive())