在 step 实现过程中,会涉及到数据通讯,step 之间,feature 之间,甚至整个执行期间的数据共享。
Python 只在定义变量的函数中保持变量有效。我们不能在定义这些变量的函数之外访问它们。但是,Behave step 实现过程,可能需要将数据从第一个函数发送到第二个函数。注意定义全局变量也不是一个好的做法。原因是,全局变量只在定义它们的文件中有用。这些不能在文件外部使用。在测试期间,我们将有许多步骤定义,我们可能需要在所有地方共享相同的信息。全局变量在这里没有任何帮助。
Behave 提供一种机制实现数据共享,今天我们就来了解一下 Behave context。
想了解更多 Behave 相关的文章,欢迎阅读《Python BDD Behave 系列》,持续更新中。
Context 是 Python Behave 中非常重要的特性,用户和 Behave 可以在其中存储共享信息。它保存测试执行期间的上下文信息。它是一个对象,可以将用户定义的数据与 Python Behave 定义的数据一起存储在上下文属性中。它运行在 Python Behave 自动管理的三个级别( feature 功能、scenario 场景和 test run 测试运行期间)上。
每当 Python Behave 启动到一个新的特性或场景时,都会向上下文添加一个新层。这允许新的活动级别添加新的值或覆盖之前为该活动期间定义的值。这可以称为作用域。
值可以在环境控制文件中定义值,例如 environment.py,它可以在 feature 级别设置,然后在某些 Scenario 场景中重写。在 Scenario 场景级别做出的更改不会永久影响在 feature 级别设置的值。在所有情况下,Context 变量都是 behave.runner.Context 的一个实例。
一个 context 对象 (behave.runner.Context)传递于:
behave runner 在测试运行期间为 context 对象分配多个属性。
注意用户不能更改 context 对象的 Behave attributes.
Attribute Name | Layer | Type | Description |
---|---|---|---|
config | test run | Configuration | Configuration that is used. |
aborted | test run | bool | Set to true if test run is aborted by the user. |
failed | test run | bool | Set to true if a step fails. |
feature | feature | Feature | Current feature. |
tags | feature, scenario | list | Effective tags of current feature, scenario, scenario outline. |
active_outline | scenario outline | Row | Current row in a scenario outline (in examples table). |
scenario | scenario | Scenario | Current scenario. |
log_capture | scenario | LoggingCapture | If logging capture is enabled. |
stdout_capture | scenario | StringIO | If stdout capture is enabled. |
stderr_capture | scenario | StringIO | If stderr capture is enabled. |
table | step | Table | Contains step’s table, otherwise None. |
text | step | String | Contains step’s multi-line text (unicode), otherwise None. |
用户可以为 context 对象分配(或修改)自定义的属性。但是,这些属性将根据定义这些属性的位置再次从上下文对象中删除。
Kind | Assign Location | Lifecycle Layer (Scope) |
---|---|---|
Hook | before_all() | test run |
Hook | after_all() | test run |
Hook | before_tags() | feature or scenario |
Hook | after_tags() | feature or scenario |
Hook | before_feature() | feature |
Hook | after_feature() | feature |
Hook | before_scenario() | scenario |
Hook | after_scenario() | scenario |
Hook | before_step() | scenario |
Hook | after_step() | scenario |
Step | Step definition | scenario |
下在通过简单的实例来了解一下 Context Attributes 应用
通过 context.text 访问多行文本 “”" “”" 之间的内容
Given user enters name and password
"""
Demo Behave
Topic – Multiline Text
"""
@given('user enters name and password')
def step_impl(context):
# access multiline text with .text attribute
print(f"Print Multiline Text: {context.text}\n")
通过 context.table 访问 table 信息
Given the following employees are registered
| Name | Department | Salary |
| Alice | HR | 50000 |
| Bob | IT | 60000 |
| Charlie | Marketing | 55000 |
@given('the following employees are registered')
def step_register_employees(context):
# Store the employee data in context.employees
context.employees = context.table
# Go through each row of the table and get the value of each row
for row in context.table:
name = row['Name']
department = row['Department']
salary = row['Salary']
print(f"Print Name:{name}, Department:{department}, Salary:{salary}")
通过 context.feature, context.scenario, context.tags 来访问 behave 属性
Then I print the context information
@then('I print the context information')
def step_print_context_information(context):
# print context.feature、context.scenario and context.tag info
print(f"Feature: {context.feature.name}")
print(f"Scenario: {context.scenario.name}")
print(f"Tag: {context.tags}")
Feature: Context Behave Attribution Example
@context_text_example
Scenario: Check login functionality
Given user enters name and password
"""
Demo Behave
Topic – Multiline Text
"""
Then user should be logged in
and I print the context information
@context_table_example
Scenario: Add employees
Given the following employees are registered
| Name | Department | Salary |
| Alice | HR | 50000 |
| Bob | IT | 60000 |
| Charlie | Marketing | 55000 |
When I view the list of employees
Then I should see the following employees
| Name | Department | Salary |
| Alice | HR | 50000 |
| Bob | IT | 60000 |
| Charlie | Marketing | 55000 |
and I print the context information
from behave import *
@given('user enters name and password')
def step_impl(context):
#access multiline text with .text attribute
print(f"Print Multiline Text: {context.text}\n")
@then('user should be logged in')
def step_impl(context):
pass
@given('the following employees are registered')
def step_register_employees(context):
# Store the employee data in context.employees
context.employees = context.table
@when('I view the list of employees')
def step_view_employee_list(context):
# Assume some action to view the list of employees
pass
@then('I should see the following employees')
def step_verify_employee_list(context):
# Assume some logic to retrieve the current list of employees
current_employees = context.employees
# Go through each row of the table and get the value of each row
for row in context.table:
name = row['Name']
department = row['Department']
salary = row['Salary']
print(f"Print Name:{name}, Department:{department}, Salary:{salary}")
# Compare the expected and current employees
assert current_employees == context.table
@then('I print the context information')
def step_print_context_information(context):
# print context.feature、context.scenario and context.tag
print(f"Feature: {context.feature.name}")
print(f"Scenario: {context.scenario.name}")
print(f"Tag: {context.tags}")
PS C:\Automation\Test> behave
Feature: Context Behave Attribution Example # BDD/Features/context_example/context_example.feature:1
@context_text_example
Scenario: Check login functionality # BDD/Features/context_example/context_example.feature:4
Given user enters name and password # BDD/steps/context_example.py:3
"""
Demo Behave
Topic – Multiline Text
"""
Print Multiline Text: Demo Behave
Topic – Multiline Text
Then user should be logged in # BDD/steps/context_example.py:8
And I print the context information # BDD/steps/context_example.py:37
Print Feature: Context Behave Attribution Example
Print Scenario: Check login functionality
Print Tag: {'context_text_example'}
@context_table_example
Scenario: Add employees # BDD/Features/context_example/context_example.feature:14
Given the following employees are registered # BDD/steps/context_example.py:12
| Name | Department | Salary |
| Alice | HR | 50000 |
| Bob | IT | 60000 |
| Charlie | Marketing | 55000 |
When I view the list of employees # BDD/steps/context_example.py:17
Then I should see the following employees # BDD/steps/context_example.py:22
| Name | Department | Salary |
| Alice | HR | 50000 |
| Bob | IT | 60000 |
| Charlie | Marketing | 55000 |
Print Name:Alice, Department:HR, Salary:50000
Print Name:Bob, Department:IT, Salary:60000
Print Name:Charlie, Department:Marketing, Salary:55000
And I print the context information # BDD/steps/context_example.py:37
Print Feature: Context Behave Attribution Example
Print Scenario: Add employees
Print Tag: {'context_table_example'}
1 feature passed, 0 failed, 0 skipped
2 scenarios passed, 0 failed, 0 skipped
7 steps passed, 0 failed, 0 skipped, 0 undefined
Took 0m0.020s
举个简单粗暴的例子吧,能说明问题就可以了,用户可以在 step 实现或 hooks 中自定义变量,注意这些变量的作用域。
Feature: Context User Attribution Example
@user_attr_1
Scenario: User Attribution Scenario 1
Given Set name as "Tom"
then the name is "Tom"
@user_attr_2
Scenario: User Attribution Scenario 2
Given Set name as "Xiao Zhang"
then the name is "Xiao Zhang"
在不同级别的 hooks 设置的变量,作用域也是不一样的。
e.g.
before_all 中定义的变量是整个运行期间都是共享的
before_feature 中定义的变量是feature 范围内共享
before_scenario 中定义的变量是 scenario 范围内共享
before_tag 中定义的变量是 feature 或 scenario 范围内共享
# environment.py
from behave import *
def before_all(context):
pass
# print("Before all tests")
context.before_all = "before all"
def before_feature(context, feature):
pass
# print(f"Before feature: {feature.name}")
context.before_feature = f"before {feature.name} feature"
def before_scenario(context, scenario):
pass
# print(f"Before scenario: {scenario.name}")
context.before_scenario = f"before {scenario.name} scenario"
def before_tag(context, tag):
pass
# print(f"Before tag: {tag}")
context.before_tag = f"before {tag} tag"
step 实现中设置了变量 context.user_name,它的作用域只在本 scenario 范围,可以在本 scenario 里 step 之间共享。
同时 step 也可以访问 hooks 自定义的变量
from behave import *
@given('Set name as {name}')
def get_user_attri(context, name):
#access multiline text with .text attribute
print(f"context.before_all: {context.before_all}")
print(f"context.before_feature: {context.before_feature}")
print(f"context.before_scenario: {context.before_scenario}")
print(f"context.before_tag: {context.before_tag}")
context.user_name = name
@then("the name is {name}")
def get_name(context, name):
assert context.user_name == name
注意 print 输出信息,需要 behave 配置文件 behave.ini 中设置 stdout_capture = false 或则执行命令 behave – no-capture
PS C:\Automation\Test> behave
Feature: Context User Attribution Example # BDD/Features/context_example/context_user_attribution_example.feature:1
@user_attr_1
Scenario: User Attribution Scenario 1 # BDD/Features/context_example/context_user_attribution_example.feature:4
Given Set name as "Tom" # BDD/steps/context_user_attribution_example.py:4
context.before_all: before all
context.before_feature: before Context User Attribution Example feature
context.before_scenario: before User Attribution Scenario 1 scenario
context.before_tag: before user_attr_1 tag
Then the name is "Tom" # BDD/steps/context_user_attribution_example.py:14
@user_attr_2
Scenario: User Attribution Scenario 2 # BDD/Features/context_example/context_user_attribution_example.feature:9
Given Set name as "Xiao Zhang" # BDD/steps/context_user_attribution_example.py:4
context.before_all: before all
context.before_feature: before Context User Attribution Example feature
context.before_scenario: before User Attribution Scenario 2 scenario
context.before_tag: before user_attr_2 tag
Then the name is "Xiao Zhang" # BDD/steps/context_user_attribution_example.py:14
1 feature passed, 0 failed, 0 skipped
2 scenarios passed, 0 failed, 0 skipped
4 steps passed, 0 failed, 0 skipped, 0 undefined
Took 0m0.003s