如何在Python中正确处理大量(格式)常量?

发布于 2025-01-10 19:34:29 字数 1294 浏览 1 评论 0原文

我正在 Python 中使用 Selenium 建立一个端到端测试框架。为了单击 DOM 元素,我必须定义许多定位器(如 XPath、CSS 选择器等),这些定位器大多是不变的。

但是,有一些常量在每次运行中都会有所不同,因此需要在运行时格式化某些内容。

目前我有一个基本的 locators.py 文件,它以视图对应的方式组织所有常量,如下所示:

class Locators:

    class LoginPage:
        USERNAME_INPUT = "//input[@id='username']"
        PASSWORD_INPUT = "//input[@id='password']"
        LOGIN_BUTTON = "//button[text()='Login']"
        # ...
    
    class Homepage:
        ACTION_BUTTON = lambda t: f"//buton[text()='{t}']"
        # ...

# ...

然后可以像这样使用:

from locators import Locators

browser.click(Locators.LoginPage.USERNAME_INPUT)
browser.click(Locators.Homepage.ACTION_BUTTON(var))

这里的最后一个示例(ACTION_BUTTON)是我最感兴趣的。由于例如 PyCharm 显示 PEP 8 警告,不鼓励使用 lambda,我想知道是否有更优雅的方法来处理此类“可格式化”常量?

此外,我希望能够将所有按钮概括为外部类,如下所示:

class Locators:

    _BUTTON = lambda s: f"//button[text()='{s}']"

    class LoginPage:
        USERNAME_INPUT = "//input[@id='username']"
        PASSWORD_INPUT = "//input[@id='password']"
        LOGIN_BUTTON = _BUTTON("Login")

    class Homepage:
        ACTION_BUTTON = _BUTTON

但这会产生语法错误。

I am setting up an end-to-end testing framework with Selenium in Python. In order to click on DOM elements I have to define a lot of locators (like XPaths, CSS-selectors etc.) which are mostly constant.

However, there are some constants that vary in each run, so some content needs to be formatted in at runtime.

At the moment I have a basic locators.py file which organizes all of the constants in a view-corresponding way like this:

class Locators:

    class LoginPage:
        USERNAME_INPUT = "//input[@id='username']"
        PASSWORD_INPUT = "//input[@id='password']"
        LOGIN_BUTTON = "//button[text()='Login']"
        # ...
    
    class Homepage:
        ACTION_BUTTON = lambda t: f"//buton[text()='{t}']"
        # ...

# ...

This can then be used like this:

from locators import Locators

browser.click(Locators.LoginPage.USERNAME_INPUT)
browser.click(Locators.Homepage.ACTION_BUTTON(var))

The last example here (ACTION_BUTTON) is of most interest to me. Since e.g. PyCharm displays a PEP 8 warning that using lambdas is discouraged I wonder whether there is a more elegant way of handling such "formattable" constants?

Furthermore, I would like to be able to generalize e.g. all buttons to the outer class like this:

class Locators:

    _BUTTON = lambda s: f"//button[text()='{s}']"

    class LoginPage:
        USERNAME_INPUT = "//input[@id='username']"
        PASSWORD_INPUT = "//input[@id='password']"
        LOGIN_BUTTON = _BUTTON("Login")

    class Homepage:
        ACTION_BUTTON = _BUTTON

But this gives a syntax error.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

乞讨 2025-01-17 19:34:29

我想这取决于个人风格。

就我个人而言,我会在此用例中使用常规格式字符串。

class Locators:

    _BUTTON = "//button[text()='{action}']"

    class LoginPage:
        USERNAME_INPUT = "//input[@id='username']"
        PASSWORD_INPUT = "//input[@id='password']"
        LOGIN_BUTTON = _BUTTON.format(action="Login")

    class Homepage:
        ACTION_BUTTON = _BUTTON

browser.click(Locators.LoginPage.USERNAME_INPUT)
browser.click(Locators.Homepage.ACTION_BUTTON.format(action=var))

I guess this is up to personal style.

Personally I would use regular format strings for this use case.

class Locators:

    _BUTTON = "//button[text()='{action}']"

    class LoginPage:
        USERNAME_INPUT = "//input[@id='username']"
        PASSWORD_INPUT = "//input[@id='password']"
        LOGIN_BUTTON = _BUTTON.format(action="Login")

    class Homepage:
        ACTION_BUTTON = _BUTTON

browser.click(Locators.LoginPage.USERNAME_INPUT)
browser.click(Locators.Homepage.ACTION_BUTTON.format(action=var))
不气馁 2025-01-17 19:34:29

我没有发现将辅助函数放在 Locators 命名空间之外有任何问题。毕竟,在执行之前它们不是定位器。

_BUTTON = lambda s: f"//button[text()='{s}']"

class Locators:

    class LoginPage:
        USERNAME_INPUT = "//input[@id='username']"
        PASSWORD_INPUT = "//input[@id='password']"
        LOGIN_BUTTON = _BUTTON("Login")

话虽这么说,我遇到了一个非常相似的问题。我通过为需要在定位器字符串中引用的变量创建一个单独的命名空间来解决这个问题。这允许我运行一些设置逻辑来确定在加载模块之前变量应该是什么。

# sys_params.py
class SystemParameters(object):
    homepage_arg1 = "default"

params = SystemParameters()
params.homepage_arg1 = "something"

# locators.py
from sys_params import params

class Locators:
    class HomePage:
        ACTION_BUTTON = f'//button[text()="{params.homepage_arg1}"]'

如果您需要模块更动态地加载(就像我所做的那样),那么您将面临必须在函数内部限定字符串范围的问题。

# locators.py
from sys_params import params

class Locators:
    class HomePage:
        @classmethod
        def ACTION_BUTTON(cls): return f'//button[text()="{params.homepage_arg1}"]'

## which gets used slightly differently
driver.click(Locators.HomePage.ACTION_BUTTON())

I don't see any issues with putting your helper functions outside the Locators namespace. Afterall, they are NOT locators until they are executed.

_BUTTON = lambda s: f"//button[text()='{s}']"

class Locators:

    class LoginPage:
        USERNAME_INPUT = "//input[@id='username']"
        PASSWORD_INPUT = "//input[@id='password']"
        LOGIN_BUTTON = _BUTTON("Login")

That being said I ran into a very similar problem. I solved this by creating a separate namespace for the variables that would need to be referenced in the locator strings. This allowed me to run a some setup logic to determine what the variables should be prior to loading the module.

# sys_params.py
class SystemParameters(object):
    homepage_arg1 = "default"

params = SystemParameters()
params.homepage_arg1 = "something"

# locators.py
from sys_params import params

class Locators:
    class HomePage:
        ACTION_BUTTON = f'//button[text()="{params.homepage_arg1}"]'

If you need the module to load a little more dynamically (like I did), then you're faced with having to scope strings inside of functions.

# locators.py
from sys_params import params

class Locators:
    class HomePage:
        @classmethod
        def ACTION_BUTTON(cls): return f'//button[text()="{params.homepage_arg1}"]'

## which gets used slightly differently
driver.click(Locators.HomePage.ACTION_BUTTON())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文