获取 access token 的地方主要取决于你的测试需求和组织方式。以下是其中几种可能的方式:
1. **在`Background`或`Given`步骤中:**如果每个`Scenario`或者大部分`Scenario`都需要 access token, 并且 token 可以在`Scenario`的生命周期中重用,那么在`Background`或`Given`步骤中获取 token 很有用。每个`Scenario`开始前,你都会获得一个新的 token。
2. **在`environment.py`文件中:**如果你想在整个`Feature`开始前就获取一次 token,并在整个`Feature`的所有`Scenario`中重用这个 token,你可以在`environment.py`文件的`before_feature`函数中获取 token。这样,即使你的`Scenario`数量很大,获取 token 的步骤也只会执行一次。
3. **在单个`Given`步骤中:**还有一种情况是你可能只有一些`Scenario`需要 access token。在这种情况下,你可以创建一个单独的`Given`步骤来获取 token,然后在需要 token 的`Scenario`中使用这个步骤。
记住,无论哪种方式,你都可以将 token 保存到`context`对象中,然后在后续步骤中从`context`对象获取并使用。例如:
~~~
pythonCopy codecontext.access_token = get_access_token()
~~~
然后在后续的步骤中:
~~~
pythonCopy codeuse_token(context.access_token)
~~~
## u
@given(u'The API endpoint is "{url}"') 这里的u 代表什么意思
这里的u是一个Python的修饰符,代表“Unicode字符串”。
在Python2中,字符串默认是ASCII编码,如果你想定义一个Unicode字符串,需要在字符串前面加上u。例如:
python
Copy code
s = u'This is a unicode string in Python 2'.
但在Python3中,所有的字符串默认都是Unicode字符串,所以这个u前缀不是必须的。
对于Behave的装饰器@given,@when和@then,无论在Python 2还是Python 3中都可以在字符串前面加上u,也可以不加。上述语句可以简化写为:
python
Copy code
@given('The API endpoint is "{url}"')
在Python 3中这样写是完全一样的结果。
## environment.py
`environment.py`是一个特殊的文件,它被 Behave 测试框架用于配置一些全局的钩子函数。这些函数会在测试运行的特定阶段自动触发,包括测试开始和结束, feature 开始和结束,以及 scenario 开始和结束。
下面是一个基本的`environment.py`文件示例:
~~~
pythonCopy code# This function is called before all tests are run
def before_all(context):
print("Doing setup before all tests...")
# This function is called after all tests are run
def after_all(context):
print("Doing teardown after all tests...")
# This function is called before each feature is run
def before_feature(context, feature):
print(f"Running feature: {feature}")
# This function is called after each feature is run
def after_feature(context, feature):
print(f"Finished feature: {feature}")
# This function is called before each scenario is run
def before_scenario(context, scenario):
print(f"Running scenario: {scenario}")
# This function is called after each scenario is run
def after_scenario(context, scenario):
print(f"Finished scenario: {scenario}")
~~~
当你运行 Behave 测试时,以上定义的函数会在适当的时机被自动调用。
注意,`context`参数是一个特殊的对象,它用于在所有的 step definitions 和钩子函数中共享数据。你可以把它看作一个字典,用于在测试运行期间存储数据。
一般来说,在钩子函数中,你可能会进行一些通用的初始化和清理工作。例如,在`before_all`函数中连接到数据库,在`after_all`函数中关闭数据库等
## Scenario Outline
~~~cucumber
Feature: Test multiple APIs
Scenario Outline: Test API get response
Given I set the API endpoint as "<endpoint>"
When I send a GET request
Then the response code should be <status_code>
Examples:
| endpoint | status_code |
~~~
`Scenario Outline`允许我们为多个API定义相同的测试步骤。然后我们在`Examples`表中为每个API定义具体的值,Behave会为表格中每一行数据生成并执行一次相应的scenario。
在 Behave 中,`Scenario Outline`是一种特殊的场景,允许对同一流程的不同参数进行测试。它以模板的方式提供一个`Scenario`,然后使用表格的形式提供参数值。每一行参数值都会被用来生成一个新的`Scenario`。
这是一个简单的例子:
~~~
cucumberCopy codeFeature: Washing clothes
Scenario Outline: Washing different types of clothes
Given I have a "<type_of_clothes>"
When I wash the clothes
Then the "<type_of_clothes>" should be clean
Examples:
| type_of_clothes |
| "cotton shirt" |
| "wool sweater" |
| "synthetic pants" |
~~~
在这个特征中,我有一个`Scenario Outline`,它说明了一个我需要测试的高级概念:洗涤不同类型的衣物。在`Examples`下面,我列出了我要测试的具体类型的衣物。
这样,对于每一行`Examples`表格中的数据,Behave 都会生成并运行一次`Scenario`。在这个例子中,Behave 将运行三个不同的`Scenario`,分别测试洗涤棉质衬衫、羊毛毛衣和合成纤维裤子,每个`Scenario`都用到了不同的`<type_of_clothes>`参数值。
使用`Scenario Outline`可以避免编写重复的行为定义,特别是当你需要测试的场景只是参数不同,而步骤基本一致的时候。
## API测试的覆盖度
API测试的覆盖范围可以根据API的特性和功能以及测试需求来决定,但通常来说,至少应该包括以下几个主要方面:
1. **请求方法**:应该测试API支持的所有HTTP方法(如GET, POST, PUT, DELETE等)。
2. **请求参数**:应该测试所有能够接受的请求参数,包括路径参数、查询参数、请求体等。
3. **验证响应**:应该验证API响应的各个组成部分,如状态码、头部信息、响应体等。并确保它们符合预期。
4. **错误处理**:要确保API在出现错误时,比如无效的请求数据或系统内部错误,能返回适当的错误信息和状态码。
以下是一个表格,列出了API测试的常见覆盖度检查项:
| 测试类型 | 描述 |
| --- | --- |
| 功能测试 | 测试API基础功能是否按预期工作,如正确创建、读取、更新和删除资源 |
| 合法性测试 | 测试API是否正确处理输入数据,如无效数据、缺失数据、不匹配的数据类型等 |
| 容错性测试 | 测试API在异常情况下的行为,如系统负载过大、网络中断等 |
| 安全性测试 | 测试API的安全防护机制,例如权限验证、数据加密等 |
| 性能测试 | 测试API在高负载或大数据量下的性能表现 |
总的来说,API测试的覆盖度应该全面,确保考虑到所有可能的请求类型、参数、状态和错误的组合,并确保API在所有情况下都能正常工作并返回预期的结果。
## 每个步骤都是唯一
在 Behave 中,每一个`@given`、`@when`和`@then`装饰的函数定义了一个特定的步骤,并且步骤的文本应该是唯一的。如果在不同的步骤定义文件中有相同文本的步骤被定义多次,Behave 会抛出错误,指出步骤已经定义过,并且通常会指明是在哪个文件中定义的。
错误信息如 "`@given('...')`has already been defined in ..." 意味着你已经有一个相同的步骤文本定义在另外一个步骤文件中。
这里有一些 Behave 的规范和最佳实践,你可以按照这些建议防止出现这类错误:
1. **确保步骤是独特的**:检查步骤的定义确保每个步骤都是唯一的。如果步骤的功能相似,考虑重构步骤以接受参数。
2. **使用描述性的文本**:给步骤赋予足够的描述性文本,以区分其他步骤。
3. **合理组织步骤文件**:步骤定义通常按照功能组织在不同的文件中。确保相关的步骤在合理的位置,并且文件命名清晰。
4. **避免重复的步骤文本**:如果多个功能文件(\*.feature)需要相似的步骤,可以考虑使用Background来共用,或者抽象成更通用的步骤。
5. **参数化步骤**:使用参数化步骤指定可变部分,而不是为相似步骤定义全新的步骤。
6. **审查代码**:在代码复审过程中注意检查是否有重复步骤的情况。
7. **使用Behave的Tags和Hooks wisely**:如果有通用代码或设置需要在不同的 scenarios 或features之间共享,考虑使用\_behave\_提供的tags和hooks系统。
为了纠正现有的问题,找到Behave报告已经定义过的步骤文本的位置,在该位置上检查步骤函数。如果该步骤在你的feature测试中是必须的,并且确实应该存在,那么需要找到并去掉那个重复的步骤定义。如果步骤被不同的测试文件共享,确保它们在共通的步骤定义文件中被定义,而且只定义一次。
## 参数化步骤
在Behave中,参数化步骤允许你用不同的数据重复执行相同的测试步骤。参数化可以通过两种方式实现:Scenario Outlines或者使用步骤函数内部的表格数据。
### 使用Scenario Outlines
`Scenario Outline`需要一个`Examples`部分来定义参数的值。下面是一个`Scenario Outline`的例子,它演示了如何用不同的参数来测试一个用户登录功能。
~~~
cucumberCopy codeFeature: Login functionality
Scenario Outline: Successful login with different users
Given the login page is displayed
When the user attempts to login with username "<username>" and password "<password>"
Then the user should be redirected to the dashboard
Examples:
| username | password |
| alice | secret |
| bob | password |
| charlie | pass123 |
~~~
每组`username`和`password`的组合都会运行同一个Scenario一次。
对应的步骤函数大概会是这样:
~~~
pythonCopy codefrom behave import given, when, then
@given('the login page is displayed')
def step_impl_display_login_page(context):
# 实现打开登录页面的操作
pass
@when('the user attempts to login with username "{username}" and password "{password}"')
def step_impl_attempt_login(context, username, password):
# 实现登录操作
# 可能需要用到 username 和 password
context.username = username
context.password = password
pass
@then('the user should be redirected to the dashboard')
def step_impl_user_redirected_to_dashboard(context):
# 确认用户被重定向到dashboard
pass
~~~
### 使用步骤函数内部的表格数据
Behave也允许在步骤中直接使用表格来实现参数化。这在`given`,`when`或`then`步骤中使用多行文本或表格数据非常有用。
例如:
~~~
cucumberCopy codeScenario: Test users with different roles
Given the following users exist
| name | role |
| Alice | admin |
| Bob | user |
When the users attempt to access the admin area
Then only users with admin role should succeed
~~~
对应的步骤文件:
~~~
pythonCopy codefrom behave import given, when, then
@given('the following users exist')
def step_impl_users_exist(context):
for row in context.table:
# 根据具体实现添加用户到你的系统或测试环境
name = row['name']
role = row['role']
# 添加用户到系统
pass
@when('the users attempt to access the admin area')
def step_impl_access_admin_area(context):
# 尝试使用各种用户角色访问管理区域
pass
@then('only users with admin role should succeed')
def step_impl_only_admins_succeed(context):
# 确认用户访问管理区域权限验证
pass
~~~
在步骤函数中,你可以通过`context.table`来遍历表格中的每一行。每行数据可以当作一个字典来处理,使用列名作为键值来访问数据。
## `tags`和`hooks`
在Behave中,`tags`和`hooks`是两个重要的功能,它们增加了测试执行的灵活性和控制力。
### Tags
`Tags`用于对 features 或 scenarios 进行分类,你可以在执行测试时使用它们来包含或排除某些测试。Tag通过在 feature 或 scenario 前加上`@`符号来定义。
以下是标签的一些用法示例:
* 标记某个特定的 Scenario:
~~~
cucumberCopy code@login
Scenario: Successful login
Given I have a user account
And I navigate to the login page
When I submit my credentials
Then I am logged in to the application
~~~
* 标记整个 Feature:
~~~
cucumberCopy code@billing
Feature: Billing process
...
~~~
你可以在运行测试时包含或排除这些标签:
~~~
bashCopy code# 只运行标记了 @billing 的测试
behave --tags=@billing
# 排除所有标记了 @wip 的测试
behave --tags=-@wip
# 你可以组合tags来更细腻地控制测试执行
behave --tags=@billing --tags=-@skip
~~~
### Hooks
`Hooks`允许你在测试的不同阶段插入自定义代码,如在测试开始前、结束后、场景开始前或结束后等。Hooks 是定义在特定的生命周期事件上的函数,它们在 Behave 的特定时间点被自动调用。
以下是在 Python 中使用 hooks 的一些示例:
* 在所有测试开始之前运行某个操作 (`before_all`hook):
~~~
pythonCopy codedef before_all(context):
'''
Perform setup actions before all tests, like initializing
the database or starting a web server.
'''
pass
~~~
* 在每个 Scenario 开始之前运行某些操作 (`before_scenario`hook):
~~~
pythonCopy codedef before_scenario(context, scenario):
'''
Perform setup actions before each scenario, like resetting
the database to a known state.
'''
pass
~~~
* 在每个 Step 开始前后运行某些操作 (`before_step`,`after_step`hooks):
~~~
pythonCopy codedef before_step(context, step):
'''
Perform some actions before each step.
'''
pass
def after_step(context, step):
'''
Perform some actions after each step, such as taking
a screenshot if the step failed.
'''
if step.status == "failed":
take_screenshot(step.name)
~~~
* 在测试结束后清理资源 (`after_all`hook):
~~~
pythonCopy codedef after_all(context):
'''
Perform cleanup actions after all tests have run, like
shutting down the web server or closing database connections.
'''
pass
~~~
Hooks 的这些自定义函数应该放在`features`目录下的`environment.py`文件中。此文件是 Behave 查找全局 setup 和 teardown 逻辑的地方。
结合使用 tags 和 hooks,可以根据复杂的逻辑来灵活地管理测试流程,并对测试的准备和收尾活动有更多的控制。
- 前言
- 1.入门篇
- Python介绍
- 安装与使用
- Python开发利器之VS Code
- 模块安装
- 命令行
- 一次Python无法安装模块的问题探索与解决之旅
- 命令运行
- Conda
- 下载地址
- 2.基础篇
- 基础语法
- 输入与输出
- with as的用法
- 注释
- Python命令行参数
- 编码
- 变量类型
- 列表遍历
- 运算符
- 表达式语句
- 条件
- 循环
- 日期和时间
- 函数
- 高级语法
- @符号-装饰器
- 模块和包
- name
- init.py
- 错误和异常
- 面向对象
- 3.专题篇
- 常用功能
- Python 字符串连接
- python web
- Python CGI编程
- Python OAuth2
- 认证 Flask-HTTPAuth
- 常用命令
- 内置函数
- dir()
- print(f)
- 标准模块
- sys
- pickle-数据序列化
- os
- IO(输入输出)
- 键盘输入
- 文件读写
- 测试
- Python测试框架之pytest快速入门
- pytest-bdd快速示例和问题解决
- 基于pytest-bdd的项目目录结构和命名规范
- python BDD 的相关概念
- Behave介绍和快速示例
- Python BDD之Behave测试报告
- Python BDD 框架比较之 pytest-bdd vs behave
- pytest进阶
- Flask + pytest测试
- 参考网址
- pytest-bdd进阶
- hehave进阶
- 测试路径
- python + selunium
- HTML 根据多层CSS 查找元素
- 等待执行
- 使用text 查找 span
- pytest如何在控制台输出
- 4.问题篇
- pip pip3 及区别
- TypeError: can only concatenate str (not "NoneType") to str
- 5.实战篇
- matplotlib-绘图包
- 导入类
- 命名规范
- 模块查找
- 6.进阶篇
- Flask
- Flask介绍
- Flask扩展模块
- Flask-Login
- 问题
- Jinja2
- Flask-RESTful
- Flask-JWT-Extended
- WSGI
- Flask-SQLAlchemy
- 部署
- Flask VS Django
- Flask Web
- Flask + Vue
- Flask实战
- Flask 标准目录结构
- Blueprints
- 参考
- FastAPI 测试
- https 证书 Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate