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 教程。