My current approach is to mock socket.socket
, listen and run my code for one connection, then stop the service.
But socket.socket.accept()
is a blocking method.
I tried to simulate socket.socket.accept()
by returning a (socket, address)
mockup tuple on the first call, then raise a socket.error
Exception
, so I can break the loop on my app that invokes socket.socket.accept()
Oddly enough all my unit tests pass but py.test
process remains active (doing what?? The debugger is not helping: It doesn't stop on any breakpoints after the second call that exits the loop as expected...) hogging resources on an endless loop until it crashes the whole system.
So, what's the right approach here?
TcpService.py
:
import socket
import threading
class TcpService:
_bind_ip = '127.0.0.1'
_bind_port = 0
_max_tcp_conn = 20
def _handle_client_connection(self):
pass
def listen_tcp(self):
self._server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._server.bind((self._bind_ip, self._tcp_bind_port))
self._server.listen(self._max_tcp_conn)
self._tcp_listen = True
while self._tcp_listen:
client_sock, address = self._server.accept() # socket.error is supposed to be raised on the second iteration during the unit test, breaking the loop
client_handler = threading.Thread(
target=self._handle_client_connection,
args=(client_sock, address)
)
client_handler.start()
test_TcpService.py
:
import unittest
import socket
from time import sleep
import mock
import TcpService
def accept_gen():
for i in range(1):
mock_socket = mock.MagicMock(name='socket.socket', spec=socket.socket)
sleep(1)
yield (mock_socket, ['0.0.0.0', 1234])
while True:
sleep(1) # so I have a chance to kill the process before the OS becomes unresponsive
yield socket.error()
class test_TcpService(unittest.TestCase):
@mock.patch('socket.socket', autospec=True)
def test_listen_tcp(self, mock_socket):
mocked_socket = mock_socket.return_value
mocked_socket.accept.side_effect = accept_gen()
sts = TcpService()
with self.assertRaises(socket.error):
sts.listen_tcp()
mock_socket.assert_called_once() # trivial POC check, final test would be more thorough...
sts.close_tcp()