PAYATHON 2026

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,专用接口的作用就会减弱。

目前没有必要仅为这几个包装方法引入复杂的继承结构。保留公共请求方法,加上资源专用方法和少量参数映射测试,就能解决当前的接口可读性问题。

备注:内容仅供参考。