如何在pytest中Mock创建BigQuery客户端的函数并完成get_tables方法测试
问题根因
你遇到的报错及测试不生效的核心原因有4点:
@mock.patch装饰的测试方法会自动将mock对象作为入参注入,你的测试方法只定义了self参数,多出来的注入参数导致参数数量不匹配报错- mock目标错误:你当前mock的是业务类
MyClass本身,实际需要mock的是MyClass内部依赖的BigQuery、Storage客户端,不需要改动业务类的逻辑 - mock返回值结构不符合业务代码要求:原
get_tables方法会遍历list_tables返回结果,取每个元素的table_id属性,直接返回字符串列表会触发属性不存在报错 - 实例化
MyClass时未传入构造函数要求的必填参数
修正后的测试代码
import pytest from unittest import mock from MyPackage import MyClass class TestMyClass: # 按你实际的MyClass所在模块路径调整patch的路径,假设MyClass定义在MyPackage/my_module.py中 @mock.patch("MyPackage.my_module.bigquery.Client") @mock.patch("MyPackage.my_module.storage.Client") def test_get_tables(self, mock_storage_client, mock_bq_client): # 构造mock的list_tables返回值,每个元素带table_id属性 mock_table1 = mock.MagicMock() mock_table1.table_id = "mock_table1" mock_table2 = mock.MagicMock() mock_table2.table_id = "mock_table2" mock_table3 = mock.MagicMock() mock_table3.table_id = "mock_table3" mock_table4 = mock.MagicMock() mock_table4.table_id = "mock_table4" mock_bq_client.return_value.list_tables.return_value = [mock_table1, mock_table2, mock_table3, mock_table4] # 实例化MyClass时传入所有必填参数,测试场景下传任意假值即可 test_obj = MyClass( project_id="test-project", pro_dataset_id="test-pro-dataset", balancing_dataset_id="test-balance-dataset", files=[] ) # 执行测试断言 assert test_obj.get_tables() == ['mock_table1', 'mock_table2', 'mock_table3', 'mock_table4'] # 可选:验证list_tables调用参数符合预期 mock_bq_client.return_value.list_tables.assert_called_once_with("test-project.test-pro-dataset")
关键修改说明
- mock的目标是业务代码实际导入使用的
bigquery.Client和storage.Client,完全屏蔽了GCP服务的实际连接逻辑 - 测试方法按装饰器顺序接收注入的mock对象(注意装饰器顺序是从下往上注入,所以storage的mock在前,bq的mock在后)
- 构造的mock返回值完全匹配业务代码的取值逻辑,不会出现属性缺失问题
- 实例化
MyClass时传入了所有必填的构造参数,不需要依赖argparse的传参逻辑,单元测试场景直接构造测试用值即可
内容的提问来源于stack exchange,提问作者Mengezi Dhlomo
相关产品推荐
相关产品推荐

