PHP Behat 库
最后修改于 2025 年 3 月 19 日
Behat 是一个用于 PHP 的行为驱动开发 (BDD) 框架。它允许您编写可读的测试来定义应用程序行为。本指南涵盖了设置、特性编写以及带有实际示例的测试执行。
安装
使用 Composer 在您的项目目录中安装 Behat 和用于小数的 Brick Math
composer require --dev behat/behat brick/math
这会将 Behat 和 Brick Math 添加为您的 PHP 项目的开发依赖项。
初始化 Behat
通过运行此命令在您的项目中初始化 Behat
vendor/bin/behat --init
这会为 Behat 设置目录结构和配置文件。
编写特性
Behat 使用 Gherkin 语法来实现可读的行为规范。在 features 目录中创建特性文件,用于实际的电子商务示例。
Feature: Shopping Cart
As a customer
To manage my purchases
I want to add items to my cart and see the total
Scenario: Add item to cart
Given I have an empty cart
When I add a "Laptop" costing 999.99
Then the cart total should be 999.99
Scenario: Add multiple items to cart
Given I have an empty cart
When I add a "Mouse" costing 29.99
And I add a "Keyboard" costing 59.99
Then the cart total should be 89.98
此特性测试电子商务应用程序的购物车功能。它定义了两个场景:添加单个项目和添加多个项目。
第一个场景确保单个项目(“笔记本电脑”)正确更新总额为 999.99。第二个测试添加“鼠标”和“键盘”,验证总额为 89.98,模拟真实的购物行为。
Feature: User Login
As a registered user
To access my account
I want to log in with my credentials
Scenario: Successful login
Given I am on the login page
When I enter username "user1" and password "pass123"
Then I should be logged in successfully
此特性测试用户身份验证,这是大多数 Web 应用程序的关键部分。它侧重于具有预定义凭据的成功登录场景。
它首先将用户置于登录页面,然后模拟输入用户名和密码。最后一步检查登录是否成功,模拟安全系统中常见的用户流程。
Feature: Order Processing
As a customer
To complete my purchase
I want to process my cart into an order
Scenario: Process cart to order
Given I have an empty cart
And I add a "Book" costing 19.99
When I process the order
Then the order total should be 19.99
此特性测试电子商务应用程序中的订单创建流程。它验证购物车中的项目是否可以转换为最终订单。
该场景从一个空购物车开始,添加一个价格为 19.99 的“书籍”,然后将其处理成一个订单。它检查订单总额是否与购物车总额匹配,确保交易正确完成。
这些特性测试购物车管理、用户登录和订单处理。
定义步骤定义
步骤定义是将 Gherkin 步骤链接到代码的 PHP 方法。使用严格类型和十进制数编辑 features/bootstrap 中的上下文类。
<?php
declare(strict_types=1);
use Behat\Behat\Context\Context;
use Brick\Math\BigDecimal;
class FeatureContext implements Context
{
private array $cart = [];
private BigDecimal $total;
private bool $isLoggedIn = false;
private string $currentPage = '';
private ?BigDecimal $orderTotal = null;
/** @Given I have an empty cart */
public function iHaveAnEmptyCart(): void
{
$this->cart = [];
$this->total = BigDecimal::zero();
}
/** @When I add a :item costing :price */
public function iAddItemCosting(string $item, string $price): void
{
$cost = BigDecimal::of($price);
$this->cart[] = ['item' => $item, 'price' => $cost];
$this->total = $this->total->plus($cost);
}
/** @Then the cart total should be :expected */
public function theCartTotalShouldBe(string $expected): void
{
$expectedTotal = BigDecimal::of($expected);
if (!$this->total->isEqualTo($expectedTotal)) {
throw new Exception("Expected $expectedTotal, got $this->total");
}
}
/** @Given I am on the login page */
public function iAmOnTheLoginPage(): void
{
$this->currentPage = 'login';
$this->isLoggedIn = false;
}
/** @When I enter username :username and password :password */
public function iEnterUsernameAndPassword(
string $username,
string $password
): void {
if ($this->currentPage !== 'login') {
throw new Exception('Not on login page');
}
$this->isLoggedIn = $username === 'user1' && $password === 'pass123';
}
/** @Then I should be logged in successfully */
public function iShouldBeLoggedInSuccessfully(): void
{
if (!$this->isLoggedIn) {
throw new Exception('Login failed');
}
}
/** @When I process the order */
public function iProcessTheOrder(): void
{
$this->orderTotal = $this->total;
$this->cart = [];
$this->total = BigDecimal::zero();
}
/** @Then the order total should be :expected */
public function theOrderTotalShouldBe(string $expected): void
{
$expectedTotal = BigDecimal::of($expected);
if ($this->orderTotal === null || !$this->orderTotal->isEqualTo($expectedTotal)) {
throw new Exception("Expected $expectedTotal, got $this->orderTotal");
}
}
}
此上下文类定义了所有三个特性的步骤,使用严格类型和 BigDecimal 实现精度。它通过购物车、总额和登录状态等属性来维护状态。
iHaveAnEmptyCart 方法将购物车和总额重置为零,为新项目做准备。它使用空数组作为购物车,并使用 zero 作为 total,确保清除。
iAddItemCosting 方法将项目及其价格添加到购物车。它将价格字符串转换为 BigDecimal,将项目存储在数组中,并通过 plus 精确加法来更新总额。
theCartTotalShouldBe 方法检查购物车总额是否与预期值匹配。它将预期字符串转换为 BigDecimal,并使用 isEqualTo 进行精确比较,如果它们不同则抛出错误。
iAmOnTheLoginPage 方法模拟导航到登录页面。它将当前页面设置为“login”,并确保用户未登录,为登录步骤提供上下文。
iEnterUsernameAndPassword 方法处理登录尝试。它检查用户是否在登录页面上,然后针对硬编码值(“user1”、“pass123”)验证凭据以简化操作。
iShouldBeLoggedInSuccessfully 方法确认登录成功。如果登录标志不为 true,则抛出异常,确保身份验证逻辑按预期工作。
iProcessTheOrder 方法将购物车总额转换为订单总额。它存储当前总额,然后重置购物车和总额,模拟真实的结帐流程。
theOrderTotalShouldBe 方法验证订单总额。它检查订单总额是否存在并且与预期的 BigDecimal 值匹配,如果任一检查失败,则抛出错误。
此上下文使用严格类型和 BigDecimal 进行精确的购物车和订单计算,以及登录逻辑。
运行 Behat 测试
使用此命令执行您的 Behat 测试
vendor/bin/behat
这会运行 features 中的所有特性文件并显示结果。
使用钩子
钩子允许您在场景周围运行代码。将它们用于设置或清理,例如在专业环境中记录测试开始。
<?php
declare(strict_types=1);
use Behat\Behat\Context\Context;
class FeatureContext implements Context
{
private string $logFile = 'test.log';
/** @BeforeScenario */
public function beforeScenario(): void
{
file_put_contents($this->logFile, "Test started\n", FILE_APPEND);
}
/** @AfterScenario */
public function afterScenario(): void
{
file_put_contents($this->logFile, "Test ended\n", FILE_APPEND);
}
}
这会将测试开始和结束时间记录到文件中,以进行审计。
Behat 最佳实践
- 编写清晰的场景: 使用简洁、描述性的步骤来提高清晰度。
- 重用步骤定义: 在特性之间共享步骤以减少冗余。
- 明智地使用钩子: 使用钩子管理资源,例如日志或数据库。
- 与 CI/CD 集成: 将 Behat 添加到 CI 管道中,以便尽早检测错误。
来源
本教程演示了如何使用 Behat 在 PHP 中进行 BDD。它提供了一种编写与业务需求一致的清晰测试的方法,有助于协作。
作者
列出 所有 PHP 教程。