Gerkin+Pytest(python)实现自动化(BDD)
将 BDD(行为驱动开发) 思想与 pytest + Python 框架结合来实现自动化测试,是一个非常强大和现代的方法。可以让我们在享有 pytest 所有强大功能(如 fixture、参数化、丰富插件)的同时,编写出业务方也能看懂的、活文档式的测试用例。
相较于unitttes+Gerkin 只需要一个插件便可以实现
目录
一、核心工具选择
二、项目目录结构
三、实现步骤
步骤 1: 编写 Gherkin 特性文件 (.feature)
步骤 2: 实现步骤定义 (Step Definitions)
步骤 3: (可选但推荐) 使用 Page Object 模式
步骤 4: 配置 Fixture (如初始化浏览器)
四、运行测试与生成报告
运行测试
生成丰富的报告
五、BDD + pytest 的优势
一、核心工具选择
要实现这个组合,核心是使用 pytest-bdd 插件。它是 pytest 的一个插件,允许你使用 Gherkin 语法编写特性文件(.feature
),并用 pytest 来执行它们。
安装所需库:
pip install pytest pytest-bdd
# 如果需要Web自动化,再安装
pip install selenium
二、项目目录结构
一个清晰的结构是成功的一半。推荐的组织方式如下:
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
中,用业务语言描述行为。
# features/login.feature
Feature: User LoginAs a user of the websiteI want to log into my accountSo that I can access my personal dashboardScenario: Successful login with valid credentialsGiven the user is on the login pageWhen the user enters a valid username and passwordAnd clicks the login buttonThen the user is redirected to the dashboardAnd a welcome message containing "Welcome" is displayedScenario Outline: Failed login attemptsGiven the user is on the login pageWhen the user enters username "<username>" and password "<password>"And clicks the login buttonThen an error message "<error_message>" should be displayedExamples:| 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
进行断言。
# 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 WebDriverreturn 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
中封装页面逻辑,使步骤定义文件更清晰、更易维护。
# pages/login_page.py
class LoginPage:def __init__(self, driver):self.driver = driverself.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").textdef get_error_message(self):# 定位错误信息元素return self.driver.find_element("css selector", "[data-test='error']").text
步骤 4: 配置 Fixture (如初始化浏览器)
在 tests/conftest.py
中定义全局的 fixture。
# 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 测试:
# 运行所有测试
pytest# 运行特定feature的测试
pytest tests/test_features/test_login.py -v# 使用pytest-bdd的特定选项,例如显示场景名称
pytest --cucumberjson=results.json # 生成Cucumber风格的JSON报告
生成丰富的报告
1. 生成 HTML 报告(推荐):
pip install pytest-html
pytest --html=report.html
2. 生成 Allure 报告(非常强大美观):
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 等工具集成。