简单的BDD Web测试
项目描述
CUCU - 简单的 BDD 网络测试
使用 gherkin 驱动各种底层工具/框架以创建真实世界测试场景的全端测试框架。
为什么选择 CUCU?
- CUCU 避免了不必要的抽象(即没有页面对象!)同时保持场景可读性。
Feature: My First Cucu Test We want to be sure the user get search results using the landing page Scenario: User can get search results Given I open a browser at the url "https://www.google.com/search" When I wait to write "google" into the input "Search" And I click the button "Google Search" Then I wait to see the text "results"
- 设计用于本地运行和在 CI 中运行
- 为您运行 selenium 容器,或者您可以使用自己的浏览器/容器
- 执行模糊匹配以近似真实用户的操作
- 提供许多开箱即用的步骤
- 使创建自定义步骤变得容易
- 启用分层配置、环境变量和 CLI 参数覆盖
- 附带一个可定制的 linter
支持文档
- CHANGELOG.md - 最新动态
- CONTRIBUTING.md - 我们如何开发和测试库
- CODE_OF_CONDUCT.md
- CONTRIBUTORS.md
- LICENSE
目录
安装
要求
Cucu 需要
- python 3.9+
- docker(用于 UI 测试)
安装指南
使用 cucu 作为测试框架设置您的存储库
- 如果您还没有安装,请安装和启动 Docker
- 安装 cucu
pip install cucu
- 创建文件夹结构并添加以下内容的文件: Cucu 使用 behave 框架,该框架期望
features/steps
目录- features/
- steps/
__init__.py
# 启用 cucu 和自定义步骤# import all of the steps from cucu from cucu.steps import * # noqa: F403, F401 # import individual sub-modules here (i.e. module names of your custom step py files) # Example: For file features/steps/ui/login.py # import steps.ui.login_steps
- environment.py - 启用前置/后置钩子
# flake8: noqa from cucu.environment import * # Define custom before/after hooks here
- features/
- 列出可用的 cucu 步骤
cucu steps
- 如果您已安装
brew install fzf
,则可以模糊查找步骤cucu steps | fzf # start typing for search
- 如果您已安装
- 创建您的第一个 cucu 测试
- features/my_first_test.feature
Feature: My First Cucu Test We want to be sure the user get search results using the landing page Scenario: User can get search results Given I open a browser at the url "https://www.google.com/search" When I wait to write "google" into the input "Search" And I click the button "Google Search" Then I wait to see the text "results"
- features/my_first_test.feature
- 运行它
cucu run features/my_first_test.feature
用法
Cucu 运行
cucu run
命令用于运行指定的测试或测试集,其最简单的用法如下
cucu run features/my_first_test.feature
这将简单地运行“搜索单词 google 的 Google”,一旦执行完成,您可以使用 cucu report
命令生成一个易于导航和阅读的 HTML 测试报告,其中包括之前的测试运行中的步骤和截图。
注意: 默认情况下,我们将直接使用您已安装的 Google Chrome
,并有一个 Python 包将处理下载与您特定的本地 Google Chrome 版本匹配的 chromedriver。
使用 docker 运行特定浏览器版本
Docker Hub 提供了易于使用的 Docker 容器,用于运行特定版本的 chrome、edge 和 firefox 浏览器进行测试,您可以在独立模式下手动启动它们,如下所示
docker run -d -p 4444:4444 selenium/standalone-chrome:latest
如果您使用的是 ARM64 CPU 架构(Mac M1 或 M2),则必须使用 seleniarm 容器。
docker run -d -p 4444:4444 seleniarm/standalone-chromium:latest
您可以选择一个特定的版本,将 latest
替换为 此处 的任何标签。您也可以以相同的方式找到 standalone-edge
和 standalone-firefox
的浏览器标签。运行命令后,您将使用 docker ps -a
看到容器正在运行并监听端口 4444
特定标签的 seleniarm:请参阅 此处
> docker ps -a
CONTAINER ID ... PORTS NAMES
7c719f4bee29 ... 0.0.0.0:4444->4444/tcp, :::4444->4444/tcp, 5900/tcp wizardly_haslett
注意: 对于 seleniarm 容器,可用的浏览器是 chromium 和 firefox。这是因为 Google 和 Microsoft 尚未为其各自的浏览器(Chrome 和 Edge)发布二进制文件。
现在当运行 cucu run some.feature
命令时,您可以提供 --selenium-remote-url https://:4444
,这样就可以在任何配置上运行一个非常特定的chrome版本。
您还可以使用位于 ./bin/start_selenium_hub.sh
的实用脚本创建一个具有所有3个浏览器节点的docker hub设置,并将测试指向 https://:4444
,然后指定 --browser
为 chrome
、firefox
或 edge
,并使用该特定浏览器进行测试。
关于seleniarm的docker hub设置:./bin/start_seleniarm_hub.sh
注意: edge
不能被选为测试的特定浏览器。
为了便于使用各种自定义设置,您还可以在本地 cucurc.yml
或更全局的位置 ~/.cucurc.yml
中设置大多数命令行选项,这些选项与相同。对于上面的远程URL,您在 cucurc.yml
中只需简单地有以下内容:
CUCU_SELENIUM_REMOTE_URL: https://:4444
然后您可以简单地运行 cucu run path/to/some.feature
,cucu
会加载本地 cucurc.yml
或 ~/.cucurc.yml
设置并使用这些设置。
扩展 Cucu
模糊匹配
cucu
使用selenium与浏览器交互,但在此基础上,我们开发了一套模糊匹配规则,允许框架通过标签和要搜索的元素类型来在页面上找到元素。
原则很简单,您想 点击“Foo”按钮
,这样我们知道您想要找到一个按钮,它可以是几种不同的HTML元素之一。
<a>
<button>
<input type="button">
<* role="button">
- 等等
我们还知道它具有您提供的名称,用作标签,这可以使用以下任何规则完成:
<thing>name</thing>
<*>name</*><thing></thing>
<thing attribute="name"></thing>
<*>name</*>...<thing>...
其中 thing
是前面识别的任何元素类型。根据上述规则,我们创建了一种简单的方法,使用这些规则来找到带有您提供的名称和要查找的元素类型的元素集合。我们目前使用 swizzle 作为底层元素查询语言,因为它高度可移植,并且比基本的CSS提供了更多有用的功能。
自定义步骤
创建自定义步骤很容易,例如
-
在您的仓库中创建一个新的Python文件
features/steps/ui/weird_button_steps.py
from cucu import fuzzy, retry, step # make this step available for scenarios and listed in `cucu steps` @step('I open the wierd menu item "{menu_item}"') def open_jupyter_menu(ctx, menu_item): # using fuzzy.find dropdown_item = fuzzy.find(ctx.browser, menu_item, ["li a"]) dropdown_item.click() # example using retry def click_that_weird_button(ctx): # using selenium's css_find_elements ctx.browser.css_find_elements("button[custom_thing='painful-id']")[0].click() @step("I wait to click this button that isn't aria compliant on my page") def wait_to_click_that_weird_button(ctx): # makes this retry with the default wait timeout retry(click_that_weird_button)(ctx) # remember to call the returned function `(ctx)` at the end
-
然后更新神奇的
features/steps/__init__.py
文件(只有一个文件!)我知道这有点奇怪,但请和我一起工作😅
# import all of the steps from cucu from cucu.steps import * # noqa: F403, F401 # import individual sub-modules here (i.e. module names of your custom step py files) # Example: For file features/steps/ui/login.py # import steps.ui.login_steps import steps.ui.weird_button_steps
-
盈利!
前置/后置钩子
有几个钩子可以访问,这里有一些
register_before_retry_hook,
register_before_scenario_hook,
register_custom_junit_failure_handler,
register_custom_tags_in_report_handling,
register_custom_scenario_subheader_in_report_handling,
register_custom_variable_handling,
register_page_check_hook,
以下是一个示例
- 将您的函数定义添加到
features/environment.py
import logging from cucu import ( fuzzy, logger, register_page_check_hook, retry, ) from cucu.config import CONFIG from cucu.environment import * def print_elements(elements): """ given a list of selenium web elements we print their outerHTML representation to the logs """ for element in elements: logger.debug(f"found element: {element.get_attribute('outerHTML')}") def wait_for_my_loading_indicators(browser): # aria-label="loading" def should_not_see_aria_label_equals_loading(): # ignore the checks on the my-page page as there are these silly # spinners that have aria-label=loading and probably shouldn't if "my-page" not in browser.get_current_url(): elements = browser.css_find_elements("[aria-label='loading'") if elements: print_elements(elements) raise RuntimeError("aria-label='loading', see above for details") retry(should_not_see_aria_label_equals_loading)() # my-attr contains "loading" def should_not_see_data_test_contains_loading(): elements = browser.css_find_elements("[my-attr*='loading'") if elements: print_elements(elements) raise RuntimeError("my-attr*='loading', see above for details") retry(should_not_see_data_test_contains_loading)() # class contains "my-spinner" def should_not_see_class_contains_my_spinner(): elements = browser.css_find_elements("[class*='my-spinner'") if elements: print_elements(elements) raise RuntimeError("class*='my-spinner', see above for details") retry(should_not_see_class_contains_my_spinner)() register_page_check_hook("my loading indicators", wait_for_my_loading_indicators)
- 完成!
自定义 lint 规则
您可以通过设置变量 CUCU_LINT_RULES_PATH
并将其指向具有类似结构的 .yaml
文件的目录来轻松扩展 cucu lint
检查规则
[unique_rule_identifier]:
message: [the message to provide the end user explaining the violation]
type: [warning|error] # I or W will be printed when reporting the violation
current_line:
match: [regex]
previous_line:
match: [regex]
next_line:
match: [regex]
fix:
match: [regex]
replace: [regex]
-- or --
delete: true
使用 current_line
、previous_line
和 next_line
部分来匹配特定的一组行,这样您就可以根据 fix
块中指定的方式修改当前行。如果没有提供 fix
块,则 cucu lint
会通知最终用户它无法修复违规行为。
在 fix
部分中,可以选择执行 match
和 replace
或简单地 delete
违规行。
更多安装 Cucu 的方法
从构建安装
在cucu目录中,您可以运行 uv build
,这将产生类似以下内容的输出
Building source distribution...
Building wheel from source distribution...
Successfully built dist/cucu-0.207.0.tar.gz and dist/cucu-0.207.0-py3-none-any.whl
在此处,您可以使用以下命令在任何位置安装文件 dist/cucu-0.1.0.tar.gz
并准备运行 cucu
工具:pip install .../cucu/dist/cucu-*.tar.gz
。
项目详情
下载文件
下载适用于您的平台文件。如果您不确定选择哪个,请了解有关安装包的更多信息。
源分发
构建分发
cucu-1.0.2.tar.gz 的哈希
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 2b396daa8fcb7f896a402e6f34e6b8755b3cfe70443dd6b81a4dc87f99af52ed |
|
MD5 | 0bd6985cf9de70970498988c87f6b840 |
|
BLAKE2b-256 | 05a72b9b75cfe31e30d45bd133122526db6e98b2c45b4d82d4e9d3fe76aab1aa |
cucu-1.0.2-py3-none-any.whl 的哈希
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 260801a91fedcdf608ba32c45196979e6fe57b34896b3c48828f469b381a4091 |
|
MD5 | f6bf1420d96121fee4b4ef35b0146a6f |
|
BLAKE2b-256 | a51925ea3fe758eb8067146c7e5b0ef96fe1e79e66415bd4f1d2ca5df76ef0c4 |