如何在类级别mock select.select?单元测试遇TypeError问题
解决类级别Mock select.select时的TypeError问题
问题原因
你遇到的TypeError: fileno() returned a non-integer本质是:类属性里定义的select_mock = patch('select.select')只是创建了一个patch对象,但没有真正激活它。此时代码里调用的select.select还是系统原生函数,它会尝试检查传入socket的fileno()方法返回值,而你的socket_mock是普通Mock对象,没有实现fileno(),所以触发报错。
而测试方法里用@patch装饰器时,装饰器会自动在测试前后启动/停止patch,把原生select.select替换成Mock对象,此时原生函数不会执行,自然不会去检查socket的fileno()。
解决方案
方案1:在类级别手动管理patch的启动与停止
修改setUpClass和tearDownClass,手动启动patch并保存补丁对象,测试完成后停止:
class ClientTestsBase(unittest.TestCase): socket_mock = mock.Mock() select_mock = None # 先初始化,后面赋值启动后的patch对象 client = None @classmethod def setUpClass(cls): # 创建并启动patch,替换原生select.select cls.select_mock = mock.patch('select.select').start() cls.select_mock.return_value = [cls.socket_mock], [], [] cls.client = Client(1, cls.socket_mock, '0.0.0.0:0') os.environ["DEFAULT_TIMEOUT_SECONDS"] = '300' @classmethod def tearDownClass(cls): # 停止patch,恢复原生select.select mock.patch.stopall()
测试方法保持不变即可:
def test_get(self): self.socket_mock.recv.return_value.decode.return_value = 'y' response = self.client.get() self.assertEqual(response, 'y')
方案2:用@patch装饰整个测试类
直接给测试类加上@patch装饰器,这样所有测试方法都会自动使用这个mock:
@mock.patch('select.select') class ClientTestsBase(unittest.TestCase): socket_mock = mock.Mock() client = None @classmethod def setUpClass(cls, select_mock): select_mock.return_value = [cls.socket_mock], [], [] cls.client = Client(1, cls.socket_mock, '0.0.0.0:0') os.environ["DEFAULT_TIMEOUT_SECONDS"] = '300' def test_get(self, select_mock): self.socket_mock.recv.return_value.decode.return_value = 'y' response = self.client.get() self.assertEqual(response, 'y')
注意:用类装饰器时,setUpClass和每个测试方法都需要接收select_mock参数,装饰器会自动注入这个mock对象。
为什么实例setUp里的尝试无效?
你之前在setUp里用@patch装饰的方式,虽然会启动patch,但Client实例是在setUpClass中创建的,此时patch还未启动,Client初始化时可能已经调用了select.select,导致原生函数被触发,依然会报错。所以必须确保在创建Client实例之前就启动patch。
内容的提问来源于stack exchange,提问作者Lee
相关产品推荐
相关产品推荐

