Python 3.5中如何正确Mock Socket的recv方法并处理settimeout失效问题
我来帮你分析并解决这两个问题,这都是Python unittest Mock中容易踩的细节坑:
问题1:print(data)显示MagicMock对象而非空字符串
这个问题的核心是没有正确Mock with语句中socket对象的__enter__方法。
原代码里用了with socket.socket(...) as client_socket:,这个语法糖会自动调用socket实例的__enter__方法,把返回值赋值给client_socket。而你的Mock代码只创建了socket类的Mock实例,但没指定__enter__的返回值,导致client_socket实际上是个未配置的MagicMock对象,它的recv().decode()自然也会返回MagicMock实例,而非你期望的空字符串。
修正方法很简单,在Mock时让__enter__返回我们配置好的socket Mock实例:
@patch('socket_example.socket.socket') def test_send_recv(self, mock_socket): # 获取socket类的实例Mock mock_socket_instance = mock_socket.return_value # 关键:让with语句中的__enter__返回这个实例本身 mock_socket_instance.__enter__.return_value = mock_socket_instance # 配置recv返回空字节 mock_socket_instance.recv.return_value = b'' # 调用被测函数 result = send_recv('<some-test-xml>') # 断言返回值为空字符串 self.assertEqual(result, '')
这样client_socket就会指向我们配置好的Mock实例,recv()返回b'',decode()后就是空字符串,print(data)就会输出空内容,而非MagicMock对象。
问题2:测试死循环需要手动停止
这个问题其实是问题1的连锁反应:因为client_socket是未正确配置的MagicMock,data是MagicMock对象,not data的结果是False(MagicMock实例在布尔判断中默认是True),导致循环条件if "<!--MessageEnd-->" in reply or not data:永远不满足,循环一直执行,所以你需要手动停止测试。
当你按照上面的方法修正Mock后,data会变成空字符串,not data为True,循环会触发break,自然就不会死循环了。
另外补充一个超时场景的测试示例,方便你验证超时分支的逻辑:
@patch('socket_example.socket.socket') def test_send_recv_timeout(self, mock_socket): mock_socket_instance = mock_socket.return_value mock_socket_instance.__enter__.return_value = mock_socket_instance # 让recv抛出超时异常 mock_socket_instance.recv.side_effect = socket.timeout result = send_recv('<some-test-xml>') self.assertIsNone(result)
最后要注意:确保你的Mock路径和被测文件的导入路径一致(比如@patch('socket_example.socket.socket')中的路径要匹配你的项目结构),Python 3.5的unittest.mock模块功能是完整的,不需要额外依赖。
备注:内容来源于stack exchange,提问作者smark

