将 BDD(行为驱动开发) 思想与 pytest + Python 框架结合来实现自动化测试,是一个非常强大和现代的方法。可以让我们在享有 pytest 所有强大功能(如 fixture、参数化、丰富插件)的同时,编写出业务方也能看懂的、活文档式的测试用例。
相较于unitttes+Gerkin 只需要一个插件便可以实现
目录
[步骤 1: 编写 Gherkin 特性文件 (.feature)](#步骤 1: 编写 Gherkin 特性文件 (.feature))
[步骤 2: 实现步骤定义 (Step Definitions)](#步骤 2: 实现步骤定义 (Step Definitions))
[步骤 3: (可选但推荐) 使用 Page Object 模式](#步骤 3: (可选但推荐) 使用 Page Object 模式)
[步骤 4: 配置 Fixture (如初始化浏览器)](#步骤 4: 配置 Fixture (如初始化浏览器))
[五、BDD + pytest 的优势](#五、BDD + pytest 的优势)
一、核心工具选择
要实现这个组合,核心是使用 pytest-bdd 插件。它是 pytest 的一个插件,允许你使用 Gherkin 语法编写特性文件(.feature
),并用 pytest 来执行它们。
安装所需库:
bash
pip install pytest pytest-bdd
# 如果需要Web自动化,再安装
pip install selenium
二、项目目录结构
一个清晰的结构是成功的一半。推荐的组织方式如下:
bash
project_root/
│
├── features/ # 存放所有的Gherkin特性文件
│ ├── login.feature
│ └── shopping_cart.feature
│
├── tests/ # 存放所有的测试代码
│ ├── __init__.py
│ ├── conftest.py # pytest的全局 fixture 配置
│ └── test_features/ # 按功能模块组织步骤定义和测试
│ ├── __init__.py
│ ├── test_login.py # 登录功能的步骤定义
│ └── test_cart.py # 购物车功能的步骤定义
│
└── pages/ # (可选)Page Object模型目录
├── __init__.py
├── base_page.py
├── login_page.py
└── cart_page.py
三、实现步骤
步骤 1: 编写 Gherkin 特性文件 (.feature)
在 features/login.feature
中,用业务语言描述行为。
python
# features/login.feature
Feature: User Login
As a user of the website
I want to log into my account
So that I can access my personal dashboard
Scenario: Successful login with valid credentials
Given the user is on the login page
When the user enters a valid username and password
And clicks the login button
Then the user is redirected to the dashboard
And a welcome message containing "Welcome" is displayed
Scenario Outline: Failed login attempts
Given the user is on the login page
When the user enters username "<username>" and password "<password>"
And clicks the login button
Then an error message "<error_message>" should be displayed
Examples:
| username | password | error_message |
| invalid_user | Pass123 | Invalid username |
| testuser | wrong | Invalid password |
| "" | Pass123 | Username is required |
步骤 2: 实现步骤定义 (Step Definitions)
在 tests/test_features/test_login.py
中,编写 Python 代码来实现每个 Gherkin 步骤。
关键点:
-
使用
@given
,@when
,@then
装饰器将函数映射到 Gherkin 步骤。 -
pytest-bdd
支持从步骤中提取参数(使用正则表达式或解析器)。 -
使用
pytest
的assert
进行断言。
python
# tests/test_features/test_login.py
import pytest
from pytest_bdd import scenarios, given, when, then, parsers
from pages.login_page import LoginPage # 导入Page Object
# 让pytest-bdd发现并加载login.feature文件
scenarios("../../features/login.feature")
# 共享 fixture:在 conftest.py 中定义 browser fixture
@pytest.fixture
def login_page(browser): # 'browser' 是另一个fixture,例如初始化Selenium WebDriver
return LoginPage(browser)
# 实现 "Given the user is on the login page" 步骤
@given("the user is on the login page")
def go_to_login_page(login_page):
login_page.load()
# 实现 "When the user enters a valid username and password"
@when("the user enters a valid username and password")
def enter_valid_credentials(login_page):
login_page.enter_username("standard_user")
login_page.enter_password("secret_sauce")
# 实现带参数的步骤(用于Scenario Outline)
@when(parsers.cfparse('the user enters username "{username}" and password "{password}"'))
def enter_credentials(login_page, username, password):
login_page.enter_username(username)
login_page.enter_password(password)
# 实现点击登录按钮的步骤(可以被多个场景复用)
@when("clicks the login button")
def click_login(login_page):
login_page.click_login()
# 实现结果验证步骤,并提取欢迎消息中的部分文本
@then(parsers.cfparse('a welcome message containing "{message_text}" is displayed'))
def verify_welcome_message(login_page, message_text):
actual_message = login_page.get_welcome_message()
assert message_text in actual_message, f"Expected '{message_text}' in message, but got '{actual_message}'"
# 实现结果验证步骤,验证错误消息
@then(parsers.cfparse('an error message "{expected_message}" should be displayed'))
def verify_error_message(login_page, expected_message):
actual_message = login_page.get_error_message()
assert actual_message == expected_message, f"Expected '{expected_message}', but got '{actual_message}'"
步骤 3: (可选但推荐) 使用 Page Object 模式
在 pages/login_page.py
中封装页面逻辑,使步骤定义文件更清晰、更易维护。
python
# pages/login_page.py
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.url = "https://www.saucedemo.com/"
def load(self):
self.driver.get(self.url)
def enter_username(self, username):
self.driver.find_element("id", "user-name").send_keys(username)
def enter_password(self, password):
self.driver.find_element("id", "password").send_keys(password)
def click_login(self):
self.driver.find_element("id", "login-button").click()
def get_welcome_message(self):
# 假设登录成功后跳转到dashboard,这里有欢迎信息
return self.driver.find_element("css selector", ".title").text
def get_error_message(self):
# 定位错误信息元素
return self.driver.find_element("css selector", "[data-test='error']").text
步骤 4: 配置 Fixture (如初始化浏览器)
在 tests/conftest.py
中定义全局的 fixture。
python
# tests/conftest.py
import pytest
from selenium import webdriver
@pytest.fixture(scope="session")
def browser():
# 初始化浏览器,这里以Chrome为例
driver = webdriver.Chrome()
driver.implicitly_wait(10)
yield driver
# 测试结束后退出浏览器
driver.quit()
# 可以在这里定义其他全局fixture
四、运行测试与生成报告
运行测试
你可以像运行普通 pytest 测试一样运行 BDD 测试:
python
# 运行所有测试
pytest
# 运行特定feature的测试
pytest tests/test_features/test_login.py -v
# 使用pytest-bdd的特定选项,例如显示场景名称
pytest --cucumberjson=results.json # 生成Cucumber风格的JSON报告
生成丰富的报告
1. 生成 HTML 报告(推荐):
bash
pip install pytest-html
pytest --html=report.html
2. 生成 Allure 报告(非常强大美观):
bash
pip install allure-pytest
pytest --alluredir=allure_results
allure serve allure_results # 在本地查看报告
五、BDD + pytest 的优势
-
业务可读性 :
.feature
文件可以作为活文档,方便产品、测试、开发三方沟通。 -
强大的 pytest 生态:你可以使用所有 pytest 的特性:fixture、参数化、标记(mark)、丰富的插件等。
-
灵活的目录结构 :不像
behave
那样强制要求固定的目录结构。 -
优秀的报告:可以生成多种格式的详细测试报告。
-
易于集成CI/CD:可以轻松地与 Jenkins、GitLab CI 等工具集成。