跳转到主要内容

pytest插件,允许您使用测试指标报告自动化操作和断言,执行纯YAML文件

项目描述

pytest-play

See Build Status on Travis CI Documentation Status https://codecov.io/gh/pytest-dev/pytest-play/branch/master/graph/badge.svg

pytest-play 是一款无需编写代码、通用、可插拔和可扩展的 自动化工具,不仅限于 测试自动化,基于出色的 pytest 测试框架,允许您通过操作和断言定义和执行包含脚本或测试场景的 YAML 文件,即使是非技术用户也可以实现和管理

  • 自动化(不一定是测试自动化)。您可以在单个文件上构建一组操作(例如,调用基于 JSON 的 API 端点,在条件匹配时执行操作)或具有许多测试场景的测试自动化项目。

    例如,您可以创建始终是最新测试数据,支持手动测试活动,构建实时模拟器等。

  • 无需编写代码,或者更准确地说,几乎无需编写代码。如果您必须编写针对操作结果的断言或某些条件表达式,您只需要具备基本的 Python 或 JavaScript 表达式知识,学习曲线平缓(类似于 variables['foo'] == 'bar'

  • 通用。它不是另一个仅适用于浏览器自动化、API 等的自动化工具。您可以使用相同的工具驱动浏览器、执行一些 API 调用、执行数据库查询,以及使用断言进行不同的技术

    因此,有多个免费或付费的测试框架或自动化工具,很多时候它们仅针对单一测试需求领域,并且不可扩展:仅 API 测试、仅 UI 测试等。如果您正在测试仅涉及 CMS 的 Web 应用程序,这可能是可以的,但如果您正在处理反应性物联网应用程序,您可能需要更多,进行跨操作或针对不同系统的跨检查,或者在 pytest-play 上构建更复杂的东西

  • 功能强大。它不是另一个测试自动化工具,它仅扩展了 pytest 框架并引入了另一种范式,并继承了众多优点(测试数据与测试实现解耦,让您可以一次编写并多次执行相同的场景,得益于原生的参数化支持、报告、与测试管理工具集成、许多有用的命令行选项、浏览器和远程 Selenium 集群集成等)

  • 可插拔和可扩展。假设您需要与一个尚未由 pytest-play 插件支持的系统进行交互,您可以自己编写或付费请他人编写。此外,还有一个脚手架工具,让您可以实施自己的命令:[https://github.com/davidemoro/cookiecutter-play-plugin](https://github.com/davidemoro/cookiecutter-play-plugin)

  • 易于使用。为什么是 YAML?易于阅读、易于编写、简单的语法、易于验证,没有括号地狱。尽管目前没有用于浏览器交互或 API 调用的录制工具,但基于非常常见模式的文档让您可以无痛苦地逐个复制、粘贴和编辑命令

  • 免费软件。它是一个基于庞大且友好的 pytest 社区的开源项目

  • 易于安装。唯一的先决条件是 Docker,多亏了 davidemoro/pytest-play Docker Hub 容器。或者更好,使用 docker,无需安装:您只需在项目文件夹中输入以下命令 docker run -i --rm -v $(pwd):/src davidemoro/pytest-play。请参阅 [https://hub.docker.com/r/davidemoro/pytest-play](https://hub.docker.com/r/davidemoro/pytest-play)

请查看页面底部扩展 pytest-play 的第三方插件

如何工作

根据您的需求和技能,您可以选择使用 pytest-play 编程编写一些 Python 代码或采用无 Python 的方法。

如前所述,使用pytest-play,您可以创建无需或仅需很少Python知识的无代码脚本或测试场景:一个文件 test_XXX.yml(例如,test_something.yml,其中 test_.yml 很重要)将被自动识别和执行,无需触碰任何 *.py 模块。

您可以使用 pytest test_XXX.yml 运行单个场景,或通过名称或关键字标记过滤整个套件。

尽管 pytest-play 从一开始就支持JSON格式,但 pytest-play>=2.0 版本将仅支持YAML以提高可用性。

无Python(纯YAML)

在这里,您可以查看一个 pytest-play 项目的内容,其中不包含任何包含登录场景的Python文件

$ tree
.
├── env-ALPHA.yml    (OPTIONAL)
└── test_login.yml

并且您可能有一些全局变量在特定目标环境的设置文件中

$ cat env-ALPHA.yml
pytest-play:
  base_url: https://www.yoursite.com

测试场景包含操作、断言和可选元数据(需要 play_selenium 外部插件)

$ cat test_login.yml
---
markers:
  - login
test_data:
  - username: siteadmin
    password: siteadmin
  - username: editor
    password: editor
  - username: reader
    password: reader
---
- comment: visit base url
  type: get
  provider: selenium
  url: "$base_url"
- comment: click on login link
  locator:
    type: id
    value: personaltools-login
  type: clickElement
  provider: selenium
- comment: provide a username
  locator:
    type: id
    value: __ac_name
  text: "$username"
  type: setElementText
  provider: selenium
- comment: provide a password
  locator:
    type: id
    value: __ac_password
  text: "$password"
  type: setElementText
  provider: selenium
- comment: click on login submit button
  locator:
    type: css
    value: ".pattern-modal-buttons > input[name=submit]"
  type: clickElement
  provider: selenium
- comment: wait for page loaded
  locator:
    type: css
    value: ".icon-user"
  type: waitForElementVisible
  provider: selenium

第一个可选的YAML文档包含一些元数据,这些元数据被称为 markers,因此您可以通过使用标记表达式调用pytest来过滤要执行的测试,解耦测试数据等。

相同的 test_login.yml 场景将使用不同的解耦测试数据 test_data 执行3次,这些测试数据定义在其第一个可选的YAML文档内部(两个 --- 线之间的块)。

所以只需编写一次,就可以用不同的测试数据多次执行!

您在这里可以看到一个hello world示例

如前所述,元数据文档是可选的,因此您的YAML文件中可能有1个或2个文档。您可以在元数据格式中找到更多信息。

在这里,您可以看到没有元数据部分的相同示例,为了完整性起见

---
- comment: visit base url
  type: get
  provider: selenium
  url: "http://YOURSITE"
- comment: click on login link
  type: clickElement
  provider: selenium
  locator:
    type: id
    value: personaltools-login
- comment: provide a username
  type: setElementText
  provider: selenium
  locator:
    type: id
    value: __ac_name
  text: "YOURUSERNAME"
- comment: provide a password
  type: setElementText
  provider: selenium
  locator:
    type: id
    value: __ac_password
  text: "YOURPASSWORD"
- comment: click on login submit button
  type: clickElement
  provider: selenium
  locator:
    type: css
    value: ".pattern-modal-buttons > input[name=submit]"
- comment: wait for page loaded
  type: waitForElementVisible
  provider: selenium
  locator:
    type: css
    value: ".icon-user"

程序化

您也可以通过程序调用pytest-play。

您可以定义一个测试 test_login.py 如此

def test_login(play):
    data = play.get_file_contents(
        'my', 'path', 'etc', 'login.yml')
    play.execute_raw(data, extra_variables={})

或者,如果您正在使用 pytest-bdd 实现基于BDD的测试,则可能使用此程序化方法。

核心命令

pytest-play 提供了一些核心命令,允许您

  • 编写简单的Python断言、表达式和变量

  • 重用步骤,包括其他测试场景脚本

  • 为特定提供者提供默认命令模板(例如,默认情况下为所有请求添加HTTP身份验证头)

  • 提供通用的等待机制。在执行依赖于前一步骤完成的后续命令之前,非常有用,等待可观察的异步事件完成其流程

您可以根据 RestrictedPython 包编写受限制的Python表达式和断言。

RestrictedPython 是一个工具,有助于定义Python语言的一个子集,该子集允许将程序输入提供给受信任的环境。RestrictedPython不是一个沙箱系统或安全环境,但它有助于定义受信任的环境并在其中执行不受信任的代码。

参见

如何重用步骤

您可以使用 include 命令分割您的命令并重用它们,以避免重复

- provider: include
  type: include
  path: "/some-path/included-scenario.yml"

您可以创建一个变量,用于存储您的测试脚本所在的基础文件夹。

默认命令

一些命令需要很多详尽的选项,您不想重复(例如,对于 play_requests 的身份验证头)。

您无需复制所有头部信息,可以使用一个以提供者名称为键,默认命令为值的 pytest-play 来初始化(此示例需要外部插件 play_selenium

- provider: python
  type: store_variable
  name: bearer
  expression: "'BEARER'"
- provider: python
  type: store_variable
  name: play_requests
  expression: "{'parameters': {'headers': {'Authorization': '$bearer'}}}"
- provider: play_requests
  type: GET
  comment: this is an authenticated request!
  url: "$base_url"

存储变量

您可以将 pytest-play 变量存储

- provider: python
  type: store_variable
  expression: "1+1"
  name: foo

编写 Python 断言

您可以根据 Python 表达式编写断言

- provider: python
  type: assert
  expression: variables['foo'] == 2

睡眠

睡眠指定秒数

- provider: python
  type: sleep
  seconds: 2

执行 Python 表达式

您可以执行一个 Python 表达式

- provider: python
  type: exec
  expression: "1+1"

条件语句和循环

如果您需要循环一系列命令或等待某物,可以使用 while 命令。当条件表达式为真时,它会执行子命令序列。假设您有一个包含整数的 countdown 变量 10,则命令块将被执行 10 次

---
- provider: python
  type: while
  expression: variables['countdown'] >= 0
  timeout: 2.3
  poll: 0.1
  sub_commands:
  - provider: python
    type: store_variable
    name: countdown
    expression: variables['countdown'] - 1

while 命令取代了其他旧命令 wait_until(当条件为真时停止)或 wait_until_not 命令。

条件命令(Python)

您可以根据以下基于 Python 的跳过条件跳过任何命令

- provider: include
  type: include
  path: "/some-path/assertions.yml"
  skip_condition: variables['cassandra_assertions'] is True

如何断言命令的执行时间

引擎为每个执行的命令更新一个名为 pytest-play 的变量 _elapsed。因此,您可以编写一些代码来

---
- type: GET
  provider: play_requests
  url: https://api.chucknorris.io/jokes/categories
  expression: "'dev' in response.json()"
- type: assert
  provider: python
  expression: "variables['_elapsed'] > 0"

生成 JUnit XML 报告

使用 --junit-xml 命令行选项,例如

--junit-xml results.xml

您将获得每个测试用例的错误、在 system-output 中执行的命令(不要使用 -s--capture=no,否则您将看不到 system-output 中的命令)和执行时间指标(全局、每个测试用例和每个单个命令,多亏了 _elapsed 属性,它在每个执行的命令中跟踪,并在 system-output 中显示)。

在这里,您可以看到一个标准的 results.xml 文件

<?xml version="1.0" encoding="utf-8"?><testsuite errors="0" failures="0" name="pytest" skipped="0" tests="1" time="0.360"><testcase classname="test_assertion.yml" file="test_assertion.yml" name="test_assertion.yml" time="0.326"><system-out>{&apos;expression&apos;: &apos;1 == 1&apos;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;assert&apos;, &apos;_elapsed&apos;: 0.0003077983856201172}
{&apos;expression&apos;: &apos;0 == 0&apos;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;assert&apos;, &apos;_elapsed&apos;: 0.0002529621124267578}
</system-out></testcase></testsuite>

生成具有自定义属性和执行时间指标的定制 JUnit XML 报告

您可以通过机器可解析的格式跟踪执行时间指标,以便进行监控和测量您认为重要的事情。例如,您可以跟踪使用

  • 响应时间(例如,返回 POST json 有效载荷需要多少时间)

  • API 调用与响应式 Web 应用程序更新或某些异步数据出现在事件存储器之间发生的时间

  • 浏览器用户输入与结果更新之间发生的时间(例如,实时搜索)

  • 登录按钮与页面加载并可使用之间发生的时间(例如,在浏览器操作后点击目标按钮需要多少时间)

在 JUnit XML 报告中跟踪响应时间指标

例如,使用命令行选项 --junit-xml report.xml 执行的 test_categories.yml 文件(需要 play_requests 插件)

test_data:
  - category: dev
  - category: movie
  - category: food
---
- type: GET
  provider: play_requests
  url: https://api.chucknorris.io/jokes/categories
  expression: "'$category' in response.json()"
- provider: metrics
  type: record_elapsed
  name: categories_time
- type: assert
  provider: python
  expression: "variables['categories_time'] < 2.5"
  comment: you can make an assertion against the categories_time

将生成一个扩展的 report.xml 文件,具有如下自定义属性

<?xml version="1.0" encoding="utf-8"?><testsuite errors="0" failures="0" name="pytest" skipped="0" tests="3" time="2.031"><testcase classname="test_categories.yml" file="test_categories.yml" name="test_categories.yml0" time="0.968"><properties><property name="categories_time" value="0.5829994678497314"/></properties><system-out>{&apos;expression&apos;: &quot;&apos;dev&apos; in response.json()&quot;, &apos;provider&apos;: &apos;play_requests&apos;, &apos;type&apos;: &apos;GET&apos;, &apos;url&apos;: &apos;https://api.chucknorris.io/jokes/categories&apos;, &apos;_elapsed&apos;: 0.5829994678497314}
{&apos;name&apos;: &apos;categories_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_elapsed&apos;, &apos;_elapsed&apos;: 3.3855438232421875e-05}
{&apos;comment&apos;: &apos;you can make an assertion against the categories_time&apos;, &apos;expression&apos;: &quot;variables[&apos;categories_time&apos;] &lt; 2.5&quot;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;assert&apos;, &apos;_elapsed&apos;: 0.0006382465362548828}
</system-out></testcase><testcase classname="test_categories.yml" file="test_categories.yml" name="test_categories.yml1" time="0.481"><properties><property name="categories_time" value="0.4184422492980957"/></properties><system-out>{&apos;expression&apos;: &quot;&apos;movie&apos; in response.json()&quot;, &apos;provider&apos;: &apos;play_requests&apos;, &apos;type&apos;: &apos;GET&apos;, &apos;url&apos;: &apos;https://api.chucknorris.io/jokes/categories&apos;, &apos;_elapsed&apos;: 0.4184422492980957}
{&apos;name&apos;: &apos;categories_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_elapsed&apos;, &apos;_elapsed&apos;: 2.09808349609375e-05}
{&apos;comment&apos;: &apos;you can make an assertion against the categories_time&apos;, &apos;expression&apos;: &quot;variables[&apos;categories_time&apos;] &lt; 2.5&quot;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;assert&apos;, &apos;_elapsed&apos;: 0.000553131103515625}
</system-out></testcase><testcase classname="test_categories.yml" file="test_categories.yml" name="test_categories.yml2" time="0.534"><properties><property name="categories_time" value="0.463592529296875"/></properties><system-out>{&apos;expression&apos;: &quot;&apos;food&apos; in response.json()&quot;, &apos;provider&apos;: &apos;play_requests&apos;, &apos;type&apos;: &apos;GET&apos;, &apos;url&apos;: &apos;https://api.chucknorris.io/jokes/categories&apos;, &apos;_elapsed&apos;: 0.463592529296875}
{&apos;name&apos;: &apos;categories_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_elapsed&apos;, &apos;_elapsed&apos;: 2.09808349609375e-05}
{&apos;comment&apos;: &apos;you can make an assertion against the categories_time&apos;, &apos;expression&apos;: &quot;variables[&apos;categories_time&apos;] &lt; 2.5&quot;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;assert&apos;, &apos;_elapsed&apos;: 0.00054931640625}
</system-out></testcase></testsuite>

并且自定义属性 categories_time 将跟踪每个测试用例执行,例如

<properties>
    <property name="categories_time" value="0.5829994678497314"/>
</properties>

JUnit XML 报告中的高级指标

在本示例中,我们希望测量页面变为可交互(页面响应用户交互)所需的时间,并评估实时搜索功能的更新时间。让我们看看 test_search.yml 的示例(需要 play_selenium)。

---
- provider: selenium
  type: get
  url: https://www.plone-demo.info/
- provider: metrics
  type: record_elapsed_start
  name: load_time
- provider: selenium
  type: setElementText
  text: plone 5
  locator:
    type: id
    value: searchGadget
- provider: metrics
  type: record_elapsed_stop
  name: load_time
- provider: metrics
  type: record_elapsed_start
  name: live_search_time
- provider: selenium
  type: waitForElementVisible
  locator:
    type: css
    value: li[data-url$="https://www.plone-demo.info/front-page"]
- provider: metrics
  type: record_elapsed_stop
  name: live_search_time

如果您使用 --junit-xml results.xml 选项执行此场景,您将获得类似于以下结果的 results.xml 文件。

<?xml version="1.0" encoding="utf-8"?><testsuite errors="0" failures="0" name="pytest" skipped="0" tests="1" time="13.650"><testcase classname="test_search.yml" file="test_search.yml" name="test_search.yml" time="13.580"><properties><property name="load_time" value="1.1175920963287354"/><property name="live_search_time" value="1.0871295928955078"/></properties><system-out>{&apos;provider&apos;: &apos;selenium&apos;, &apos;type&apos;: &apos;get&apos;, &apos;url&apos;: &apos;https://www.plone-demo.info/&apos;, &apos;_elapsed&apos;: 9.593282461166382}
{&apos;name&apos;: &apos;load_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_elapsed_start&apos;, &apos;_elapsed&apos;: 1.1682510375976562e-05}
{&apos;locator&apos;: {&apos;type&apos;: &apos;id&apos;, &apos;value&apos;: &apos;searchGadget&apos;}, &apos;provider&apos;: &apos;selenium&apos;, &apos;text&apos;: &apos;plone 5&apos;, &apos;type&apos;: &apos;setElementText&apos;, &apos;_elapsed&apos;: 1.1019845008850098}
{&apos;name&apos;: &apos;load_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_elapsed_stop&apos;, &apos;_elapsed&apos;: 1.9788742065429688e-05}
{&apos;name&apos;: &apos;live_search_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_elapsed_start&apos;, &apos;_elapsed&apos;: 1.0013580322265625e-05}
{&apos;locator&apos;: {&apos;type&apos;: &apos;css&apos;, &apos;value&apos;: &apos;li[data-url$=&quot;https://www.plone-demo.info/front-page&quot;]&apos;}, &apos;provider&apos;: &apos;selenium&apos;, &apos;type&apos;: &apos;waitForElementVisible&apos;, &apos;_elapsed&apos;: 1.060795545578003}
{&apos;name&apos;: &apos;live_search_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_elapsed_stop&apos;, &apos;_elapsed&apos;: 2.3603439331054688e-05}
</system-out></testcase></testsuite>

在这种情况下,您会发现关键指标 load_time1.11 秒,而 live_search_time1.09 秒,如下所示。

<properties>
    <property name="load_time" value="1.1175920963287354"/>
    <property name="live_search_time" value="1.0871295928955078"/>
</properties>

因此,借助 JUnit XML 报告,您可以使用机器可读格式跟踪响应时间(不仅限于基于浏览器的计时),以便在无法直接在测试系统上跟踪计时的情况下,以可接受的近似值由第三方系统摄取。

使用表达式跟踪 JUnit XML 报告中的任何属性

让我们看看 test_categories.yml(需要 play_selenium)。

test_data:
  - category: dev
  - category: movie
  - category: food
---
- type: GET
  provider: play_requests
  url: https://api.chucknorris.io/jokes/categories
  expression: "'$category' in response.json()"
- provider: metrics
  type: record_property
  name: categories_time
  expression: "variables['_elapsed']*1000"
- type: assert
  provider: python
  expression: "variables['categories_time'] < 2500"
  comment: you can make an assertion against the categories_time

使用 --junit-xml results.xml 命令行选项生成一些自定义属性(以毫秒为单位的 categories_time 使用 Python 表达式)。

<?xml version="1.0" encoding="utf-8"?><testsuite errors="0" failures="0" name="pytest" skipped="0" tests="3" time="2.312"><testcase classname="test_categories.yml" file="test_categories.yml" name="test_categories.yml0" time="1.034"><properties><property name="categories_time" value="610.3124618530273"/></properties><system-out>{&apos;expression&apos;: &quot;&apos;dev&apos; in response.json()&quot;, &apos;provider&apos;: &apos;play_requests&apos;, &apos;type&apos;: &apos;GET&apos;, &apos;url&apos;: &apos;https://api.chucknorris.io/jokes/categories&apos;, &apos;_elapsed&apos;: 0.6103124618530273}
{&apos;expression&apos;: &quot;variables[&apos;_elapsed&apos;]*1000&quot;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;exec&apos;, &apos;_elapsed&apos;: 0.0006859302520751953}
{&apos;expression&apos;: &quot;variables[&apos;_elapsed&apos;]*1000&quot;, &apos;name&apos;: &apos;categories_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_property&apos;, &apos;_elapsed&apos;: 0.006484270095825195}
{&apos;comment&apos;: &apos;you can make an assertion against the categories_time&apos;, &apos;expression&apos;: &quot;variables[&apos;categories_time&apos;] &lt; 2500&quot;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;assert&apos;, &apos;_elapsed&apos;: 0.0005526542663574219}
</system-out></testcase><testcase classname="test_categories.yml" file="test_categories.yml" name="test_categories.yml1" time="0.550"><properties><property name="categories_time" value="443.72105598449707"/></properties><system-out>{&apos;expression&apos;: &quot;&apos;movie&apos; in response.json()&quot;, &apos;provider&apos;: &apos;play_requests&apos;, &apos;type&apos;: &apos;GET&apos;, &apos;url&apos;: &apos;https://api.chucknorris.io/jokes/categories&apos;, &apos;_elapsed&apos;: 0.44372105598449707}
{&apos;expression&apos;: &quot;variables[&apos;_elapsed&apos;]*1000&quot;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;exec&apos;, &apos;_elapsed&apos;: 0.0009415149688720703}
{&apos;expression&apos;: &quot;variables[&apos;_elapsed&apos;]*1000&quot;, &apos;name&apos;: &apos;categories_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_property&apos;, &apos;_elapsed&apos;: 0.01613616943359375}
{&apos;comment&apos;: &apos;you can make an assertion against the categories_time&apos;, &apos;expression&apos;: &quot;variables[&apos;categories_time&apos;] &lt; 2500&quot;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;assert&apos;, &apos;_elapsed&apos;: 0.0011241436004638672}
</system-out></testcase><testcase classname="test_categories.yml" file="test_categories.yml" name="test_categories.yml2" time="0.676"><properties><property name="categories_time" value="576.5485763549805"/></properties><system-out>{&apos;expression&apos;: &quot;&apos;food&apos; in response.json()&quot;, &apos;provider&apos;: &apos;play_requests&apos;, &apos;type&apos;: &apos;GET&apos;, &apos;url&apos;: &apos;https://api.chucknorris.io/jokes/categories&apos;, &apos;_elapsed&apos;: 0.5765485763549805}
{&apos;expression&apos;: &quot;variables[&apos;_elapsed&apos;]*1000&quot;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;exec&apos;, &apos;_elapsed&apos;: 0.0006375312805175781}
{&apos;expression&apos;: &quot;variables[&apos;_elapsed&apos;]*1000&quot;, &apos;name&apos;: &apos;categories_time&apos;, &apos;provider&apos;: &apos;metrics&apos;, &apos;type&apos;: &apos;record_property&apos;, &apos;_elapsed&apos;: 0.006584644317626953}
{&apos;comment&apos;: &apos;you can make an assertion against the categories_time&apos;, &apos;expression&apos;: &quot;variables[&apos;categories_time&apos;] &lt; 2500&quot;, &apos;provider&apos;: &apos;python&apos;, &apos;type&apos;: &apos;assert&apos;, &apos;_elapsed&apos;: 0.0005452632904052734}
</system-out></testcase></testsuite>

获取每次执行要跟踪的指标,例如

<properties><property name="categories_time" value="610.3124618530273"/></properties>

因此,您可以跟踪每个测试执行的类别,或您想要的任何内容。

使用 statsd/graphite 监控测试指标

如果您喜欢测量一切的方法,您可以在正常测试执行或高负载/压力测试期间,从最终用户的角度跟踪和监控有趣的定制测试指标,这要归功于 statsd/graphite 集成。

衡量重要关键指标的原因有很多

  • 使用过去跟踪的同一指标的统计信息,在相同条件下比较不同版本的性能(不再仅仅是说系统今天 似乎更慢

  • 预测前端许多项目下的系统行为(例如,评估浏览器处理由无限滚动插件管理的成千上万的项目)

  • 预测系统在高负载下的行为

您可以使用 Docker 在几分钟内安装 statsd/graphite

基本上,您可以在 statsd/graphite 上跟踪每个 数值 指标,就像我们将要看到的那样,使用用于跟踪 JUnit XML 报告中指标的相同命令。

此外,但不是必需的,安装名为 pytest-statsd 的第三方插件。您可以在 statsd/graphite 上跟踪

  • 执行时间

  • 每个状态的执行测试数量(通过,失败,错误等)

先决条件(您需要安装默认未安装的可选 statsd 客户端):

pip install pytest-play[statsd]

用法(与 pytest-statsd 兼容的 CLI 选项)

--stats-d [--stats-prefix play --stats-host http://myserver.com --stats-port 3000]

其中

  • --stats-d,启用 statsd

  • --stats-prefix(可选),如果您计划将多个项目的结果发送到同一服务器。例如,如果您提供 play 作为前缀,您将获得一个时间指标,其键为 stats.timers.play.YOURMETRIC.mean(或您可以使用 .mean.upperupper_90 等))

  • --stats-host,默认为 localhost

  • 默认情况下,--stats-port8125

现在您可以使用之前看到的 record_elapsedrecord_elapsed_start/record_elapsed_stop 命令来跟踪时间指标(pytest-play 会为您发送所需转换为毫秒的时间值到 statsd)。

如果您想使用 record_property 命令跟踪自定义指标,您必须提供一个名为 metric_type 的附加参数。例如:

- provider: metrics
  type: record_property
  name: categories_time
  expression: "variables['_elapsed']*1000"
  metric_type: timing
- provider: metrics
  type: record_property
  name: fridge_temperature
  expression: "4"
  metric_type: gauge

关于 record_property 命令的一些附加信息

  • 如果您在 record_property 命令中没有提供 metric_type 选项,则值将不会发送到 statsd(如果提供了 --junit-xml 选项,最终它们将在 JUnit XML 报告中跟踪)。

  • 如果您提供了一个允许的 metric_type 值(timinggauge),则非数字值将被视为错误(将引发 ValueError 异常)。

  • 不允许的 metric_type 值将被视为错误。

  • 如果您提供 timing 作为 metric_type,则您需要提供一个以毫秒为单位的数字值。

监控 HTTP 响应时间

监控 API 响应时间(见 https://github.com/pytest-dev/pytest-play/tree/features/examples/statsd_graphite_monitoring

Chuck Norris API response time

浏览器指标

使用 Selenium 从最终用户的角度监控浏览器指标(见 https://github.com/pytest-dev/pytest-play/tree/features/examples/statsd_graphite_monitoring_selenium

  • 从页面加载到页面可用

  • 实时搜索响应性

Time for first interaction after load and live search rendering timings

以编程方式记录指标

如果您不想使用 pytest-play 但需要记录测试指标,您可以将 pytest-play 作为库使用:

def test_programmatically(play):
    play.execute_command({
        'provider': 'metrics',
        'type': 'record_property',
        'name': 'oil_temperature',
        'expression': '60',
        'metric_type': 'gauge'})

使用 pytest-play 和 bzt/Taurus(BlazeMeter)进行性能测试

您可以将所有 pytest-play 场景重用,并使用 bzt/Taurus(与 BlazeMeter 兼容,并具有所有其优点)将其转换为性能测试。

添加一个不带 test_ 前缀的 bzt/Taurus YAML 文件(完整示例在此处 bzt_performance

settings:
  artifacts-dir: /tmp/%Y-%m-%d_%H-%M-%S.%f

execution:
- executor: pytest
  scenario: pytest-run
  iterations: 1

scenarios:
  pytest-run:
    # additional-args: --stats-d --stats-prefix play
    script: scripts/

services:
- module: shellexec
  prepare:
  - pip3 install -r https://raw.githubusercontent.com/davidemoro/pytest-play-docker/master/requirements.txt

然后运行以下命令

docker run --rm -it -v $(pwd):/src --user root --entrypoint "bzt" davidemoro/pytest-play bzt.yml

您将看到 bzt 正在运行,正在播放我们的场景

Taurus/bzt running pytest-play scenarios

您可以通过取消注释 additional-args 来传递其他 pytest 命令行选项(例如,启用 statsd 以监控关键用户指标或任何其他 cli 选项)。

bzt/Taurus 的更多信息请在这里

在有效负载中动态表达式而不声明变量

如果您必须向 REST 终端或包含动态值的 MQTT 消息发送某个有效负载,您可以使用 store_variable 存储一个变量,并在需要时在有效负载中使用 $variable_name。存储变量很酷,如果您稍后会重用该值,但是如果您只需要生成一个动态值,例如毫秒级的时间戳,您可以使用 {! EXPRESSION !} 格式。

例如(《a href="https://github.com/davidemoro/play_mqtt" rel="nofollow">play_mqtt 插件需要)

---
- comment: python expressions in mqtt payload (without declaring variables)
  provider: mqtt
  type: publish
  host: "$mqtt_host"
  port: "$mqtt_port"
  endpoint: "$mqtt_endpoint/$device_serial_number"
  payload: '{
        "measure_id":   [124],
        "obj_id_L":     [0],
        "measureType":  ["float"],
        "start_time":   {! int(datetime.datetime.utcnow().timestamp()*1000) !},
        "bin_value":    [1]
    }'

其中,表达式

{! int(datetime.datetime.utcnow().timestamp()*1000) !},

将被打印

1553007973702

基于浏览器的命令

pytest-play 核心不再包含基于浏览器的命令。已移动到 play_selenium 外部插件。

pytest-play 是可插拔和可扩展的。

pytest-play 拥有可插拔的架构,您可以扩展它。

例如,您可能希望支持自己的命令,支持非 UI 命令,如执行原始 POST/GET 等调用,模拟物联网设备活动,提供与复杂 UI 小部件(如日历小部件)的简单交互,通过实现二进制协议的串行端口向设备发送命令等。

如何注册新的命令提供者

假设您想使用以下命令扩展 pytest-play

command = {'type': 'print', 'provider': 'newprovider', 'message': 'Hello, World!'}

您只需实现一个命令提供者

from pytest_play.providers import BaseProvider

class NewProvider(BaseProvider):

    def this_is_not_a_command(self):
        """ Commands should be command_ prefixed """

    def command_print(self, command):
        print(command['message'])

    def command_yetAnotherCommand(self, command):
        print(command)

并在您的 setup.py 中注册新提供者,添加一个入口点

entry_points={
    'playcommands': [
        'print = your_package.providers:NewProvider',
    ],
},

您也可以为非 UI 命令定义新的提供者。例如,发布 MQTT 消息以模拟物联网设备活动进行集成测试。

如果您愿意,可以使用以下工具生成新的命令提供者

元数据格式

您还可以添加一些场景元数据,在 test_XXX.yml 上方放置另一个 YAML 文档,格式如下

---
markers:
  - marker1
  - marker2
test_data:
  - username: foo
  - username: bar
---
# omitted scenario steps in this example...

选项详情

  • markers,您可以使用标记装饰您的场景,并使用标记表达式(如 -m "marker1 and not slow")在 pytest 命令行中过滤要执行的场景

  • test_data,启用解耦测试数据的参数化,并允许您执行相同的场景多次。例如,上面的示例将执行两次(一次使用“foo”用户名,另一次使用“bar”)

将在下一个功能中添加新选项(例如,跳过场景,xfail,xpass 等)。

示例

文章和演讲

文章

演讲

第三方 pytest-play 插件

  • play_seleniumpytest-play 插件,在底层使用 Selenium/Splinter 驱动浏览器。Selenium grid 兼容,并具有隐式自动等待操作,以实现更健壮的场景和更少的控制。

  • play_requestspytest-play 插件,驱动著名的 Python requests 库进行 HTTP 调用。

  • play_sqlpytest-play 对 SQL 表达式和断言的支持

  • play_cassandrapytest-play 对 Cassandra 表达式和断言的支持

  • play_dynamodbpytest-play 对 AWS DynamoDB 查询和断言的支持

  • play_websocketpytest-play 对 WebSocket 的支持

  • play_mqtt,用于支持MQTT的pytest-play插件。感谢play_mqtt,您可以使用它测试一个模拟的IoT设备(该设备通过MQTT发送命令)与具有UI检查的响应式Web应用程序之间的集成。

    您还可以构建一个生成消息的模拟器。

请随时通过pull request添加您自己的公共插件!

Twitter

pytest-play的推文在这里

更新日志

2.3.1 (2019-06-12)

错误修复

  • 修复与pytest 4.6的兼容性。参考#86

文档

  • 更新媒体部分(文章和演讲)

2.3.0 (2019-04-05)

功能和改进

  • wait_untilwait_until_not现在可以接受没有sub_commands属性的命令

  • 在Python提供者中实现新的while命令(while表达式为真时)

2.2.2 (2019-03-29)

小改动

  • 在引擎上移除内部属性参数

错误修复

  • 添加对pytest-repeat的--count命令行选项的兼容性

文档

  • 说明如何使用{! expr !}表达式生成动态值(例如,在REST或MQTT中不需要存储变量时动态负载)

2.2.1 (2019-03-19)

小改动

  • 添加Python表达式中的内置类型int和float

  • 使Python表达式更具灵活性,以供未来改进(内部更改,不会影响兼容性)

错误修复

  • 修复--setup-plan调用

文档

  • 添加更多示例(bzt/Taurus和pytest-play的性能测试)

2.2.0 (2019-03-01)

  • statsd集成(可选要求)用于使用statsd/graphite的高级测试指标。如果您使用pytest-play[statsd]安装pytest play以启用可选的statsd支持,您将获得额外的依赖项statsd客户端,并且可以使用由pytest-statsd插件定义的相同cli选项(例如,--stats-d [--stats-prefix myproject --stats-host http://myserver.com --stats-port 3000])。

    请注意:尽管上述cli选项与pytest-statsd插件定义的相同,但在撰写本文时,pytest-statsd不是pytest-play的依赖项,因此您不会获得失败次数、通过次数等的统计信息,而只有pytest-play跟踪的统计信息。如果您需要它们,您可以安装pytest-statsd(它与pytest-play兼容)

2.1.0 (2019-02-22)

功能

  • 支持使用system-out元素生成JUnit xml生成文件,用于每个测试用例执行(pytest --junit-xml选项)。默认情况下,除非您使用--capture=no或其别名-s,否则system-out将在JUnit报告中跟踪。

  • 如果启用system-out,则在--junit-xml报告中跟踪每个执行命令的_elapsed时间

  • --junit-xml报告中跟踪pytest自定义属性,用于监控和衡量您关心的内容。例如,您可以使用机器可解析的格式跟踪作为关键指标的时间,例如在上一操作结束和下一操作完成之间发生的时间。基本上,您可以在property_name load_login键下跟踪在点击提交按钮和当前命令结束之间发生的时间(例如,点击菜单或可接收文本的文本输入),使用机器可解析的格式。

    属性名 property_name 的值耗时将作为标准 pytest-play 变量可用,以便您可以进行额外的断言

  • 每次命令执行后,都会添加/更新一个 pytest-play 变量,报告耗时(可通过 variables['_elapsed'] 访问)。

    因此请注意,_elapsed 变量名应被视为一个特殊变量,因此您不应使用此名称来存储变量

  • 在断言失败或命令错误的情况下改进调试。记录变量转储在标准日志和 system-out 中(如果可用)

  • 在断言错误的情况下改进调试性(记录失败的表达式)

  • 添加了一个新的 metrics 提供程序,允许您与 --junit-xml 选项结合跟踪自定义指标。您可以用机器可读的格式跟踪响应时间、动态自定义表达式、不同命令之间发生的时间(例如,测量登录后与页面交互所需的时间、异步更新之前的时间等)。在 metrics 提供程序下,您会发现 record_propertyrecord_elapsedrecord_elapsed_startrecord_elapsed_stop 命令

文档

  • 较小的文档更改

  • 添加更多示例

2.0.2 (2019-02-06)

文档

  • 更多示例

  • 修复 README 中的文档错误(基于 selenium 的示例缺少 provider: selenium

2.0.1 (2019-01-30)

文档

  • 在 README 中提及 davidemoro/pytest-play docker 容器。现在您可以使用以下 docker 命令使用 pytest-play: docker run -i --rm -v $(pwd):/src davidemoro/pytest-play

错误修复

  • 修复由于 pytest-play 依赖项约束不存在而导致的 pipenv 锁定错误(RestrictedPython>=4.0.b2 -> RestrictedPython>=4.0b2)

2.0.0 (2019-01-25)

重大更改

  • 将 fixture 重命名为 play_jsonplay (#5)

  • 删除 json 支持,仅采用 yaml 格式进行场景 (#5)

  • 删除 .ini 文件用于元数据,如果您需要它们,可以在场景 .yml 文件之上添加 YAML 文档。现在您不再需要多个文件来装饰场景了 (#65)

  • play.execute 不再接受原始数据字符串),消耗命令列表。引入了接受原始数据字符串的 play.execute_raw

  • play.execute_command 现在接受 Python 字典(而不是字符串)

  • Selenium 提供程序已从 pytest-play 核心中删除,在单独的包 play_selenium 中实现。从现在开始,您必须将 provider: selenium 添加到您的 selenium 命令中

  • engine 的 parametrizer_class 属性不再可用(现在默认使用 parametrizer.Parametrizer

错误修复

  • 修复 PyPI 上的无效标记 (#55)

  • 修复无效转义序列 (#62)。

文档和微小更改

  • 添加示例文件夹

1.4.2 (2018-05-17)

  • 在 Github 上的配置更改。使用 pytest 采取的相同分支策略(master 变为 main 分支,见 #56)

  • 修复跳过的测试并添加新测试(取消选择具有关键字和标记表达式的场景)

  • 修复 #58:如果您尝试在自动发现模式下启动 pytest-play,则不再会收到 TypeError

  • 修复 #55:重构文本 lint 在 README.rst 上(在 pypi 上的可视化不良)

  • 更新 README(文章和演讲链接)

  • play_json fixture 添加 DeprecationWarning。pytest-play 将基于 yaml 而不是 json(版本 >=2.0.0)。请参阅 https://github.com/pytest-dev/pytest-play/issues/5

1.4.1 (2018-04-06)

  • 文档改进

  • 添加 bzt/Taurus/BlazeMeter 兼容性

1.4.0 (2018-04-05)

  • 较小的文档改进

  • 现在自动收集和执行 test_XXX.json 文件

  • 您可以使用pytest CLI运行测试场景 pytest test_YYY.json

  • 引入了包含标记定义的json测试场景ini文件。对于给定的test_YYY.json场景,您可以添加一个test_YYY.ini ini文件

    [pytest]
    markers =
        marker1
        marker2

    并使用标记表达式过滤场景 pytest -m marker1

  • 启用了在场景ini文件中对纯json场景的参数化

    [pytest]
    test_data =
       {"username": "foo"}
       {"username": "bar"}

    您的json场景将执行两次

  • pytest-play现在基于您的pytest-variables文件中可选的pytest-play部分的包含内容加载一些变量。所以如果您的变量文件包含以下值

    pytest-play:
      foo: bar
      date_format: YYYYMMDD

    您将能够使用表达式$foo$date_formatvariables['foo']variables['date_format']

1.3.2 (2018-02-05)

  • 在Python表达式中添加sorted

1.3.1 (2018-01-31)

  • 添加更多测试

  • 更新文档

  • play_json fixture不再假设您有某些pytest-variables设置。不再强制

  • 修复仅在Windows上发生的包含场景的bug(正斜杠与反斜杠和JSON解码问题)

1.3.0 (2018-01-22)

  • 改进文档

  • 支持拆卸回调

1.2.0 (2018-01-22)

  • pytest-play中实现基于Python的命令,并弃用play_python。因此,此功能是play-python插件的直接替换。

    从现在起,您不再需要安装play_python

  • 更新文档

  • 弃用selenium命令(它们将在单独的插件中实现,并在pytest-play >= 2.0.0中弃用)。所有您之前的脚本都将正常工作,此警告只是为了那些直接导入提供者的人。

  • 实现跳过条件。您可以省略任何评估基于Python的跳过条件的命令的执行

1.1.0 (2018-01-16)

  • 更新文档(添加新的pytest play插件)

  • 支持命令提供者的默认有效载荷。对于HTTP身份验证头和常见数据库设置非常有用

1.0.0 (2018-01-10)

  • 执行命令现在接受kwargs

  • 执行命令现在返回命令值

  • 完全重构include提供者(无向后兼容性)

  • 添加play_json.get_file_contents并删除data_getter fixture(无向后兼容性)

0.3.1 (2018-01-04)

  • play引擎现在记录要执行的命令和错误

0.3.0 (2018-01-04)

  • 您可以在执行命令时更新变量

  • 您可以通过setuptools entrypoints使用来自第三方包的新可插拔命令扩展pytest-play

0.2.0 (2018-01-02)

  • 默认情况下不再打开浏览器 pytest-play是一个通用测试引擎,也可以用于非UI测试。

    因此,对于非UI测试(例如:API测试),无需打开浏览器

0.1.0 (2017-12-22)

  • 实现可重用步骤(包括场景)

  • 较小的文档更改

0.0.1 (2017-12-20)

  • 首次发布

项目详细信息


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源代码分发

pytest-play-2.3.1.tar.gz (53.5 kB 查看哈希值)

上传时间 源代码

由以下提供支持