BDD 即 Behavior-driven development,行为驱动开发。
在软件工程中, BDD是一种敏捷开发流程。减少传统测试过程中由于技术背景能力,非技术与商业参与者之间业务理解不同而导致的问题。BDD它关注的核心是设计,其要求在设计测试用例的时候对系统进行定义,倡导使用通用的语言将系统的行为描述出来,将系统设计和测试用例结合起来,从而以此为驱动进行开发工作。
BDD行为驱动是一种敏捷开发模式, 重点在于消除开发/测试对需求了解的歧义及用户场景的验证。
BDD 使用自然语言Gherkin来描述系统功能和场景,根据这些描述步骤进行系统自动化的测试。
官方文档地址: https://pytest-bdd.readthedocs.io/en/latest/
Git仓库地址:https://gitlink.org.cn/test_framework/pytest-bdd
pytest-bdd 是一个BDD测试框架,类似于behave, cucumber。与许多其他传统框架pytest, unittest不同,BDD框架-pytest-bdd 不需要单独的运行程序,它可以统一单元测试和功能测试,减轻连续集成服务器配置的负担,并允许重用测试。
pytest-bdd作为pytest的一个插件,所有pytest的功能和插件都可以用于pytest-bdd!
为单元测试编写的pytest固定装置可以通过依赖项注入重新用于功能步骤中提到的设置和操作。允许对需求进行真正的BDD说明,而无需维护任何包含Gherkin命令性声明的上下文对象。
使用如下命令可以安装:pip install pytest-bdd
我们先大概介绍以下框架结构,大家可以先有一个概念。了解了框架结构后,我们再详细介绍pytest-bdd的使用。
正常情况下,我们需要有一个目录features
来管理测试用例(.feature
文件),一个目录step_defs
来管理测试步骤(test_*.py文件)。
features
可以自定义名称,以及自定义位置。只需要在测试步骤(test_*.py文件)中引用正确即可。step_defs
也可以自定义名称以及自定义位置。以下是示例:
# 第一种
├────features/ # 用户场景
│ ├────projects/
│ │ ├────create_project.features
│ ├────login.features
├────step_defs/ # 步骤函数和测试场景
│ ├────projects/
│ │ ├────test_create_project.py
│ └────test_login.py
# 第二种
├────cases/ # 用户场景
│ ├────projects/
│ │ ├────create_project.features
│ ├────login.features
├────test_cases/ # 步骤函数和测试场景
│ ├────projects/
│ │ ├────test_create_project.py
│ └────test_login.py
# 第三种
├────test_cases/ # 测试用例
│ ├────features/ # 用户场景
│ │ ├────login.features
│ ├────step_defs/ # 步骤函数和测试场景
│ │ ├────test_login.py
# 第四种
├────features/ # 用户场景
│ ├────projects/
│ │ ├────create_project.features
│ ├────login.features
├────step_defs/ # 步骤函数
│ ├────projects/
│ │ ├────create_project_steps.py
│ └────login_steps.py
├────test_case/ # 测试场景
│ ├────test_projects/
│ │ ├────test_create_project.py
│ └────test_login.py
BDD提供一套标准的需求及用户场景表达语法, 使用文件后缀为.feature
的文件进行管理。
BDD分如下几部分:
注意:
.feature
文件如下所示是一个示例(文件名:login.feature):
Feature: 登录模块
需求描述: 用户进入登录页面/弹窗登录页面,使用正确用户名以及密码可以登录成功
Background: 清除浏览器缓存,避免缓存影响用例
Given 清除浏览器缓存
Scenario: 弹窗登录: 正确用户名和密码登录成功
Given 打开浏览器,访问项目首页
When 点击:登录按钮,打开登录弹窗
And 弹窗中,输入用户:xxxxxx, 密码:12345678
And 弹窗中,点击: 登录按钮, 提交登录表单
Then 当前页面的url地址应该是:https://www.gitlink.org.cn/explore
And 右上角显示的用户昵称应该是:xxxxxx
Scenario: 网页登录: 正确用户名和密码登录成功
Given 打开浏览器,访问GitLink首页
When 点击:登录按钮,进入登录页面
And 登录页面中,输入用户:xxxxxx, 密码:12345678
And 登录页面中,点击: 登录按钮, 提交登录表单
Then 当前页面的url地址应该是:https://www.gitlink.org.cn/xxxxxx
And 右上角显示的用户昵称应该是:xxxxxx
在前面的章节我们已经实现了测试场景,但是单单是靠测试场景是无法运行测试的。 我们还需要将每个场景文件.feature
中的描述翻译成具体的页面操作, 每一句对应一个步骤函数。
我们需要从pytest_bdd
中导入given, when, then, parsers
,帮助我们实现用户场景中的每一个步骤。
注意:我这里只摘取了上述部分步骤进行实现展示。方法都是类似的。
比如我们实现given中步骤,只需要写一个步骤函数,使用@given("步骤名称")
装饰器即可。
@given("打开浏览器,访问项目首页")
def visit_projects_home(driver, host):
driver.get(host)
同时,一个步骤函数是可以添加多个步骤的,例如:
@when("点击:登录按钮,进入登录页面")
@when("点击:登录按钮,打开登录弹窗")
def click_login_button(driver):
login_button = driver.find_element(By.XPATH, "//a[text()='登录']")
login_button.click()
如果我们需要从测试步骤弹窗中,输入用户:xxxxxx, 密码:12345678
中提取用户名和密码的具体值,我们可以使用parsers
来解析语句中的参数。例如:
@when(parsers.parse("弹窗中,输入用户:{username}, 密码:{password}"))
def input_login_info_on_pop(driver, username, password):
# 这里通过parsers.parse解析到username=xxxxxx, password=12345678
username_input = driver.find_element(By.XPATH, "//input[@name='username']")
password_input = driver.find_element(By.XPATH, "//input[@name='password']")
username_input.send_keys(username)
password_input.send_keys(password)
而且在步骤函数中,我们可以直接使用pytest
的fixture。
# conftest.py 中定义了一个fixture
@pytest.fixture(scope="session")
def host():
return GLOBAL_VARS.get("host")
# --------------------------------------------------------------#
# test_*.py 测试方法中直接传入fixture的名称作为参数使用即可。
# 这里直接传入了fixture的名称: host, 作为测试方法:visit_projects_home的入参使用。
@given("打开浏览器,访问项目首页")
def visit_projects_home(driver, host):
driver.get(host)
特别注意:步骤函数中的步骤描述,需要于.feature
中的保持完全一致,否则可能会报错。
scenarios
或@scenario
关联用户场景和步骤函数前面我们已经分别使用.feature
文件以及.py
文件分别实现了用户场景以及步骤函数。 我们需要将这两者关联起来。
假如我们的步骤函数是放在test_
开头的.py
文件中,我们可以直接在该文件中使用scenarios
或@scenario
关联用户场景和步骤函数。
假如我们的步骤函数不是放在test_
开头的.py
文件中,我们可以新建一个test_
开头的.py
来管理测试场景,但是我们需要导入该测试场景中所需要相关的步骤函数。
我这里是直接在test_
开头的.py
文件中放置步骤函数以及测试场景。
下面我将分别介绍scenarios
和@scenario
装饰器,这两种不同的方法来定义和标记 BDD 场景。
scenarios
scenarios
,用于将多个 BDD 场景与步骤函数关联起来。它可以接受一个或多个参数,每个参数代表一个 BDD 场景。通过 scenarios
,可以将多个场景组织在一起,并且可以在步骤函数中使用场景名称进行条件判断或其它处理。
我们可以使用scenarios
的方式建立,参考如下:
# test_login.py
fr