python主函数和内部函数的执行逻辑

之前写个函数是这样定义的

def test_TC41306_create_contact_point(alarm_contact_point_page: Page):
    alarm_contact_point_page.pause()
    alarm_contact_point_page.get_by_role("button", name="创建联络点").click()
    alarm_contact_point_page.get_by_placeholder("请输入名称").fill("test_Webhook")
    alarm_contact_point_page.locator("form i").click()
    alarm_contact_point_page.locator("li").filter(has_text="Webhook").click()
    alarm_contact_point_page.get_by_placeholder("请输入内容").fill("http://172.17.140.249/")
    alarm_contact_point_page.get_by_role("button", name="保存联络点").click()
    expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").to_be_in_viewport()
    expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").not_to_be_in_viewport()
    alarm_contact_point_page.get_by_role("row", name="test_Webhook").get_by_role("button").click()
    alarm_contact_point_page.locator("tr", has_text="test_Webhook").locator("a", has_text="编辑").click()
    alarm_contact_point_page.wait_for_timeout(1000)
    CommonUtil.assert_screenshot(alarm_contact_point_page.get_by_role("dialog", name="编辑联络点"), [], "test_TC41306_create_contact_point_1.png",True)
    alarm_contact_point_page.get_by_label("close 编辑联络点").click()


    alarm_contact_point_page.get_by_role("button", name="创建联络点").click()
    alarm_contact_point_page.get_by_placeholder("请输入名称").fill("test_Email")
    alarm_contact_point_page.locator("form i").click()
    alarm_contact_point_page.locator("li").filter(has_text="Email").click()
    alarm_contact_point_page.get_by_placeholder("请输入内容").fill("yuling.zhang@greatdb.com")
    alarm_contact_point_page.get_by_role("button", name="保存联络点").click()
    expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").to_be_in_viewport()
    expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").not_to_be_in_viewport()
    alarm_contact_point_page.get_by_role("row", name="test_Email").get_by_role("button").click()
    alarm_contact_point_page.locator("tr", has_text="test_Email").locator("a", has_text="编辑").click()
    alarm_contact_point_page.wait_for_timeout(1000)
    CommonUtil.assert_screenshot(alarm_contact_point_page.get_by_role("dialog", name="编辑联络点"), [], "test_TC41306_create_contact_point_2.png",True)
    alarm_contact_point_page.get_by_label("close 编辑联络点").click()



    alarm_contact_point_page.get_by_role("button", name="创建联络点").click()
    alarm_contact_point_page.get_by_placeholder("请输入名称").fill("test_Syslog")
    alarm_contact_point_page.locator("form i").click()
    alarm_contact_point_page.locator("li").filter(has_text="Syslog").click()
    alarm_contact_point_page.get_by_placeholder("请输入内容").first.fill("172.17.140.249")
    alarm_contact_point_page.locator("textarea").fill("CONTROL|+|平台|+|1002|+|1001|+|{{ address }}|+|{{ hostname }}|+|(RECOVER) {{ alertname }}|+|{{ resource }}|+|{{ value }}|+|DB|+|GREATDB|+|{{ resource_type }}|+|{{ severity_int }}|+|触发值: {{ value }} {{ expr_condition }} 告警阈值: {{ threshold_value }}|+|{{ timestamp }}")
    alarm_contact_point_page.get_by_placeholder("请输入内容").nth(2).fill("20MNIBUS")
    alarm_contact_point_page.get_by_label("描述文字").first.fill("11")
    alarm_contact_point_page.get_by_label("描述文字").nth(1).fill("12")
    alarm_contact_point_page.get_by_label("描述文字").nth(2).fill("13")
    alarm_contact_point_page.get_by_label("描述文字").nth(3).fill("14")
    alarm_contact_point_page.get_by_label("描述文字").nth(4).fill("15")
    alarm_contact_point_page.get_by_role("radio", name="false").click()
    alarm_contact_point_page.get_by_role("button", name="保存联络点").click()
    expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").to_be_in_viewport()
    expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").not_to_be_in_viewport()
    alarm_contact_point_page.get_by_role("row", name="test_Syslog").get_by_role("button").click()
    alarm_contact_point_page.locator("tr", has_text="test_Syslog").locator("a", has_text="编辑").click()
    alarm_contact_point_page.wait_for_timeout(1000)
    CommonUtil.assert_screenshot(alarm_contact_point_page.get_by_role("dialog", name="编辑联络点"), [], "test_TC41306_create_contact_point_3.png",True)
    alarm_contact_point_page.get_by_label("close 编辑联络点").click()



    alarm_contact_point_page.get_by_role("button", name="创建联络点").click()
    alarm_contact_point_page.get_by_placeholder("请输入名称").fill("test_SMC")
    alarm_contact_point_page.locator("form i").click()
    alarm_contact_point_page.locator("li").filter(has_text="SMC").click()
    alarm_contact_point_page.get_by_placeholder("请输入内容").first.fill("1")
    alarm_contact_point_page.get_by_placeholder("请输入内容").nth(1).fill("2")
    alarm_contact_point_page.get_by_placeholder("请输入内容").nth(2).fill("1")
    alarm_contact_point_page.get_by_placeholder("请输入内容").nth(3).fill("2")
    alarm_contact_point_page.get_by_placeholder("请输入内容").nth(4).fill("http://172.17.140.249/")
    alarm_contact_point_page.get_by_role("button", name="保存联络点").click()
    expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").to_be_in_viewport()
    expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").not_to_be_in_viewport()
    alarm_contact_point_page.get_by_role("row", name="test_SMC").get_by_role("button").click()
    alarm_contact_point_page.locator("tr", has_text="test_SMC").locator("a", has_text="编辑").click()
    alarm_contact_point_page.wait_for_timeout(1000)
    CommonUtil.assert_screenshot(alarm_contact_point_page.get_by_role("dialog", name="编辑联络点"), [], "test_TC41306_create_contact_point_4.png",True)
    alarm_contact_point_page.get_by_label("close 编辑联络点").click()

这里有非常多的重复操作,可以简单的修改为这样的写法

def test_TC41306_create_contact_point(alarm_contact_point_page: Page):
    """
    测试创建不同类型的联络点。
    """
    def create_and_validate_contact_point(name: str, contact_type: str, content: dict, screenshot_name: str):
        """
        创建联络点并验证保存和编辑操作。

        :param name: 联络点名称
        :param contact_type: 联络点类型(如 "Webhook", "Email", "Syslog", "SMC")
        :param content: 联络点内容(字典形式,键为字段类型,值为内容列表)
        :param screenshot_name: 截图文件名
        """
        # 点击“创建联络点”按钮
        alarm_contact_point_page.get_by_role("button", name="创建联络点").click()

        # 填写名称
        alarm_contact_point_page.get_by_placeholder("请输入名称").fill(name)

        # 选择联络点类型
        alarm_contact_point_page.locator("form i").click()
        alarm_contact_point_page.locator("li").filter(has_text=contact_type).click()

        # 填写内容
        for field_type, field_values in content.items():
            if field_type == "placeholder":
                for placeholder_text, value, index in field_values:
                    if index == 0:
                        alarm_contact_point_page.get_by_placeholder(placeholder_text).first.fill(value)
                    else:
                        alarm_contact_point_page.get_by_placeholder(placeholder_text).nth(index).fill(value)
            elif field_type == "textarea":
                alarm_contact_point_page.locator("textarea").fill(field_values)
            elif field_type == "label":
                for index, value in enumerate(field_values):
                    if index == 0:
                        alarm_contact_point_page.get_by_label("描述文字").first.fill(value)
                    else:
                        alarm_contact_point_page.get_by_label("描述文字").nth(index).fill(value)
            elif field_type == "radio":
                alarm_contact_point_page.get_by_role("radio", name=field_values).click()

        # 保存联络点
        alarm_contact_point_page.get_by_role("button", name="保存联络点").click()

        # 验证保存成功的提示信息
        expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").to_be_in_viewport()
        expect(alarm_contact_point_page.get_by_text("保存成功"), "未弹出提示信息").not_to_be_in_viewport()

        # 编辑联络点并截图
        alarm_contact_point_page.get_by_role("row", name=name).get_by_role("button").click()
        alarm_contact_point_page.locator("tr", has_text=name).locator("a", has_text="编辑").click()
        CommonUtil.assert_screenshot(alarm_contact_point_page.get_by_role("dialog", name="编辑联络点"), [], screenshot_name, True)
        alarm_contact_point_page.get_by_label("close 编辑联络点").click()

    # 测试创建 Webhook 联络点
    create_and_validate_contact_point(
        name="test_Webhook",
        contact_type="Webhook",
        content={
            "placeholder": [("请输入内容", "http://172.17.140.249/", 0)]
        },
        screenshot_name="test_TC41306_create_contact_point_1.png"
    )

    # 测试创建 Email 联络点
    create_and_validate_contact_point(
        name="test_Email",
        contact_type="Email",
        content={
            "placeholder": [("请输入内容", "yuling.zhang@greatdb.com", 0)]
        },
        screenshot_name="test_TC41306_create_contact_point_2.png"
    )

    # 测试创建 Syslog 联络点
    create_and_validate_contact_point(
        name="test_Syslog",
        contact_type="Syslog",
        content={
            "placeholder": [("请输入内容", "172.17.140.249", 0), ("请输入内容", "20MNIBUS", 1)],
            "textarea": "CONTROL|+|平台|+|1002|+|1001|+|{{ address }}|+|{{ hostname }}|+|(RECOVER) {{ alertname }}|+|{{ resource }}|+|{{ value }}|+|DB|+|GREATDB|+|{{ resource_type }}|+|{{ severity_int }}|+|触发值: {{ value }} {{ expr_condition }} 告警阈值: {{ threshold_value }}|+|{{ timestamp }}",
            "label": ["11", "12", "13", "14", "15"],
            "radio": "false"
        },
        screenshot_name="test_TC41306_create_contact_point_3.png"
    )

    # 测试创建 SMC 联络点
    create_and_validate_contact_point(
        name="test_SMC",
        contact_type="SMC",
        content={
            "placeholder": [
                ("请输入内容", "1", 0),
                ("请输入内容", "2", 1),
                ("请输入内容", "1", 2),
                ("请输入内容", "2", 3),
                ("请输入内容", "http://172.17.140.249/", 4)
            ]
        },
        screenshot_name="test_TC41306_create_contact_point_4.png"
    )

疑问

为什么要写成这样 ???????????

def test_TC41306_create_contact_point(alarm_contact_point_page: Page):
    """
    测试创建不同类型的联络点。
    """
    def create_and_validate_contact_point(name: str, contact_type: str, content: dict, screenshot_name: str): 而不是只定义一个函数

这是一个很好的问题!将代码拆分为 ​主函数 和 ​内部函数 的设计是为了提高代码的 ​可读性、复用性 和 ​可维护性。以下是对这种设计的具体解释:

代码复用性
在测试 test_TC41306_create_contact_point 中,创建和验证联络点的逻辑是重复的(如填写名称、选择类型、填写内容、保存、验证提示信息、编辑并截图等)。如果将这些逻辑直接写在主函数中,会导致大量重复代码。

通过将这部分逻辑提取到一个内部函数 create_and_validate_contact_point 中,可以实现代码复用。每次测试不同类型的联络点时,只需调用这个内部函数并传入不同的参数即可。

可读性
将复杂逻辑拆分为多个函数,可以使代码更清晰、更易读。主函数 test_TC41306_create_contact_point 专注于测试流程的组织,而内部函数 create_and_validate_contact_point 专注于具体的操作和验证逻辑。
这种分层设计使得代码更易于理解:

​主函数:描述测试的整体流程(如测试哪些联络点类型)。
​内部函数:描述具体的操作和验证逻辑(如如何创建和验证联络点)。

这里我们再解释下 主函数和内部函数的执行逻辑

代码结构

def test_TC41306_create_contact_point(alarm_contact_point_page: Page):
    # 内部函数定义
    def create_and_validate_contact_point(...):
        ...

    # 调用内部函数 4 次
    create_and_validate_contact_point(...)  # Webhook
    create_and_validate_contact_point(...)  # Email
    create_and_validate_contact_point(...)  # Syslog
    create_and_validate_contact_point(...)  # SMC

​执行逻辑详解

​步骤 1:主函数启动

当测试框架(如 Pytest)运行 test_TC41306_create_contact_point 时:

主函数 test_TC41306_create_contact_point 被调用。
参数 alarm_contact_point_page(Playwright 的 Page 对象)被传入。

​步骤 2:内部函数定义

主函数内部定义了 create_and_validate_contact_point 函数:

​此时内部函数只是被定义,并未执行。
它像一个“工具包”,等待被调用。

​步骤 3:调用内部函数

主函数依次调用 create_and_validate_contact_point 4 次,每次传入不同的参数:

python
create_and_validate_contact_point(name=“test_Webhook”, …) # 第一次调用
create_and_validate_contact_point(name=“test_Email”, …) # 第二次调用
create_and_validate_contact_point(name=“test_Syslog”, …) # 第三次调用
create_and_validate_contact_point(name=“test_SMC”, …) # 第四次调用

​步骤 4:内部函数执行

每次调用 create_and_validate_contact_point 时:

根据传入的参数(如 name=“test_Webhook”),执行以下操作:
点击“创建联络点”按钮。
填写名称。
选择联络点类型。
填写内容。
保存并验证提示信息。
编辑联络点并截图。
执行完成后,返回到主函数,继续下一次调用。

​参数传递示例

以 ​Webhook 联络点 为例:

create_and_validate_contact_point(
    name="test_Webhook",
    contact_type="Webhook",
    content={"placeholder": [("请输入内容", "http://172.17.140.249/", 0)]},
    screenshot_name="test_TC41306_create_contact_point_1.png"
)
​参数如何生效:
name="test_Webhook" → 联络点名称是 test_Webhook。
contact_type="Webhook" → 选择类型为 Webhook。
content → 填写 Webhook 的 URL。
screenshot_name → 截图保存为 test_TC41306_create_contact_point_1.png。

​执行顺序流程图

主函数 内部函数 浏览器 调用(Webhook 参数) 创建 Webhook 联络点 完成操作 返回 调用(Email 参数) 创建 Email 联络点 完成操作 返回 调用(Syslog 参数) 创建 Syslog 联络点 完成操作 返回 调用(SMC 参数) 创建 SMC 联络点 完成操作 返回 主函数 内部函数 浏览器

5. ​为什么不用一个函数?

如果将所有逻辑写在一个函数中:

def test_TC41306_create_contact_point(...):
    # 创建 Webhook 的代码(重复 4 次)
    # 创建 Email 的代码(重复 4 次)
    # 创建 Syslog 的代码(重复 4 次)
    # 创建 SMC 的代码(重复 4 次)
  

​缺点:
​代码冗余:相同逻辑重复多次。
​难以维护:修改一个地方需要改 4 次。
​可读性差:代码臃肿,难以理解。
通过拆分为主函数和内部函数:

​优点:
​复用代码:只需写一次核心逻辑。
​参数驱动:通过参数控制不同行为。
​易于扩展:新增联络点类型只需加一行调用。
6. ​总结
​主函数 是测试用例的入口,负责组织测试流程。
​内部函数 是具体操作的实现,通过参数复用逻辑。
​执行顺序:主函数调用内部函数 4 次,每次传入不同参数。
​参数作用:告诉内部函数“创建什么类型的联络点”。
这样设计后,代码更简洁、更易维护。如果有其他疑问,欢迎继续提问! 😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值