跳转到主要内容

简单的BDD Web测试

项目描述

Cucu Logo CUCU - 简单的 BDD 网络测试

使用 gherkin 驱动各种底层工具/框架以创建真实世界测试场景的全端测试框架。

CircleCI

为什么选择 CUCU?

  1. 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"
    
  2. 设计用于本地运行和在 CI 中运行
  3. 为您运行 selenium 容器,或者您可以使用自己的浏览器/容器
  4. 执行模糊匹配以近似真实用户的操作
  5. 提供许多开箱即用的步骤
  6. 使创建自定义步骤变得容易
  7. 启用分层配置、环境变量和 CLI 参数覆盖
  8. 附带一个可定制的 linter

支持文档

  1. CHANGELOG.md - 最新动态
  2. CONTRIBUTING.md - 我们如何开发和测试库
  3. CODE_OF_CONDUCT.md
  4. CONTRIBUTORS.md
  5. LICENSE

目录

安装

要求

Cucu 需要

  1. python 3.9+
  2. docker(用于 UI 测试)

安装指南

使用 cucu 作为测试框架设置您的存储库

  1. 如果您还没有安装,请安装和启动 Docker
  2. 安装 cucu
    pip install cucu
    
  3. 创建文件夹结构并添加以下内容的文件: 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
      
  4. 列出可用的 cucu 步骤
    cucu steps
    
    • 如果您已安装 brew install fzf,则可以模糊查找步骤
      cucu steps | fzf
      # start typing for search
      
  5. 创建您的第一个 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"
      
  6. 运行它
    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-edgestandalone-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,然后指定 --browserchromefirefoxedge,并使用该特定浏览器进行测试。

关于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.featurecucu 会加载本地 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提供了更多有用的功能。

自定义步骤

创建自定义步骤很容易,例如

  1. 在您的仓库中创建一个新的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
    
  2. 然后更新神奇的 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
    
  3. 盈利!

前置/后置钩子

有几个钩子可以访问,这里有一些

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,

以下是一个示例

  1. 将您的函数定义添加到 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)
    
  2. 完成!

自定义 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_lineprevious_linenext_line 部分来匹配特定的一组行,这样您就可以根据 fix 块中指定的方式修改当前行。如果没有提供 fix 块,则 cucu lint 会通知最终用户它无法修复违规行为。

fix 部分中,可以选择执行 matchreplace 或简单地 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 (349.3 kB 查看哈希)

上传时间

构建分发

cucu-1.0.2-py3-none-any.whl (257.1 kB 查看哈希)

上传时间 Python 3

支持