Python 子类如何封装 API 专用参数,是否需要单独测试?
建议在子类中增加专用方法
如果设计中已经区分 DashboardManager 和 UserManager,建议分别提供 get_dashboards()、get_users() 等专用方法,继续复用父类的 get_omd_assets()。
方法即使只有几行,也能固定 endpoint、明确支持哪些参数,并将 Python 参数名转换为 API 参数名。调用方可以直接写 get_users(is_bot=False),不用记住 /users 和 isBot 的组合。
get_users() 是新增方法,并没有重写 get_omd_assets()。父类处理公共请求逻辑,子类提供资源相关接口。对于需要自由指定 endpoint 的通用工具,直接使用 get_omd_assets() 也合理。
如何封装参数
可以保留现有父类,让子类通过显式参数调用它:
class UserManager(OMDManager):
def get_users(
self,
limit: int = 100,
is_bot: bool = False,
) -> list:
params = {
"isBot": "true" if is_bot else "false",
}
return self.get_omd_assets(
endpoint="/users",
limit=limit,
params=params,
)
调用时:
users = user_manager.get_users(is_bot=False)
bot_users = user_manager.get_users(limit=25, is_bot=True)
这里沿用了问题中的 /users,并假设 API 要求 isBot 使用小写字符串 "true"、"false"。原始调用示例传入的是 "false",而原先拟议的封装传入了 Python 布尔值。两者能否产生相同的请求,取决于 OpenMetadataClient 如何序列化查询参数。如果客户端已经负责转换,封装中可以直接传递布尔值;具体应按 API 和客户端的实际约定选择。
limit 通过父类的同名参数传入即可,不必再放入 params。现有父类先设置 limit,再执行 query_params.update(params),因此 params 中同名的 limit 会覆盖前者。避免同时传两份,可以减少调用歧义。
DashboardManager 也可以采用相同方式,显式接收 service,再映射为 "service" 查询参数。它的 endpoint 和参数类型应使用项目中的实际定义。
get_users() 仍然值得测试
get_omd_assets() 的测试只能证明公共请求逻辑符合预期,无法证明 get_users() 使用了正确的 endpoint、参数名和默认值。例如,把 "isBot" 误写为 "is_bot",通常不会让父类测试失败。
测试范围可以按职责划分:
- get_omd_assets():验证通用参数合并、默认 limit、客户端调用和返回值传递。
- get_users():验证
/users、is_bot 到isBot的映射、默认值以及显式参数的传递。
get_users() 不需要重复覆盖父类的全部参数合并场景,也不必在每个子类中重测分页实现。如果分页由 api_request_pages() 负责,分页细节应主要在该客户端的测试中验证。
测试示例
下面以 pytest 和 unittest.mock 为例,假设 UserManager 已在测试模块中导入,并采用上面的小写字符串转换方式:
from unittest.mock import Mock
import pytest
@pytest.mark.parametrize(
"kwargs, expected_params",
[
({}, {"limit": 100, "isBot": "false"}),
(
{"limit": 25, "is_bot": True},
{"limit": 25, "isBot": "true"},
),
],
)
def test_get_users_builds_expected_request(kwargs, expected_params):
client = Mock()
expected_users = [{"id": "user-1"}]
client.api_request_pages.return_value = expected_users
manager = UserManager(client)
result = manager.get_users(**kwargs)
client.api_request_pages.assert_called_once_with(
endpoint="/users",
params=expected_params,
)
assert result == expected_users
这个测试执行真实的 get_users() 和 get_omd_assets(),只替换发起请求的客户端。它检查专用方法最终生成的请求参数,不会发起网络请求。
也可以 mock get_omd_assets(),只检查 get_users() 对它的调用。这种写法隔离得更细,但会依赖内部委托方式。当前的封装比较薄,在客户端边界检查最终参数通常更直观。
保持封装范围清晰
先公开实际需要的参数即可。以后增加过滤条件时,再为专用方法添加明确参数。如果很快又要求调用方传入任意 params,调用方仍需自行了解底层 API,专用接口的作用就会减弱。
目前没有必要仅为这几个包装方法引入复杂的继承结构。保留公共请求方法,加上资源专用方法和少量参数映射测试,就能解决当前的接口可读性问题。
备注:内容仅供参考。
