BDD - Python Behave 数据共享 context

BDD - Python Behave 数据共享 context

  • 引言
  • Behave context
  • Context Attributes
    • Behave Attributes
    • User Attributes
  • Context Attributes 应用
    • Context behave attributes 应用
      • context.text
      • context.table
      • context.feature, context.scenario, context.tags
      • 完整的例子
        • feature 文件
        • step 文件
        • 执行结果
    • Context user attributes 应用
      • feature 文件
      • hooks environment.py 文件
      • step 文件
      • 执行结果

引言

在 step 实现过程中,会涉及到数据通讯,step 之间,feature 之间,甚至整个执行期间的数据共享。

Python 只在定义变量的函数中保持变量有效。我们不能在定义这些变量的函数之外访问它们。但是,Behave step 实现过程,可能需要将数据从第一个函数发送到第二个函数。注意定义全局变量也不是一个好的做法。原因是,全局变量只在定义它们的文件中有用。这些不能在文件外部使用。在测试期间,我们将有许多步骤定义,我们可能需要在所有地方共享相同的信息。全局变量在这里没有任何帮助。

Behave 提供一种机制实现数据共享,今天我们就来了解一下 Behave context。

想了解更多 Behave 相关的文章,欢迎阅读《Python BDD Behave 系列》,持续更新中。

Behave context

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 Attributes

一个 context 对象 (behave.runner.Context)传递于:

  • step definitions (step implementations)
  • behave hooks (before_all(), before_feature(), …, after_all())

Behave Attributes

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.

User Attributes

用户可以为 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 Attributes 应用

Context behave attributes 应用

context.text

通过 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

通过 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

通过 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 文件
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    
step 文件
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

Context user attributes 应用

举个简单粗暴的例子吧,能说明问题就可以了,用户可以在 step 实现或 hooks 中自定义变量,注意这些变量的作用域。

feature 文件

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 environment.py 文件

在不同级别的 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 文件

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

你可能感兴趣的:(#,Behave,python,Behave,BDD,context)