本文目录导读:

我来详细讲解PHP中使用Behat进行行为驱动开发(BDD)测试的方法。
Behat简介
Behat是PHP的BDD测试框架,用于测试应用程序的业务行为,它允许你用人类可读的语言描述功能,然后自动验证。
安装Behat
使用Composer安装
# 创建项目目录 mkdir my-project cd my-project # 初始化composer composer init # 安装Behat composer require --dev behat/behat # 初始化Behat项目结构 vendor/bin/behat --init
项目结构
初始化后,Behat会创建以下结构:
my-project/
├── features/
│ ├── bootstrap/
│ │ └── FeatureContext.php
│ └── *.feature
├── vendor/
├── behat.yml
└── composer.json
创建Feature文件
示例功能:用户登录
features/login.feature
Feature: 用户登录
作为用户
我需要能够登录系统
以便访问我的账户
Scenario: 使用正确的凭据登录
Given 我在登录页面
When 我输入用户名 "john@example.com"
And 我输入密码 "password123"
And 我点击登录按钮
Then 我应该看到欢迎消息 "欢迎,John"
And 我应该被重定向到仪表盘
Scenario: 使用错误的凭据登录
Given 我在登录页面
When 我输入用户名 "john@example.com"
And 我输入密码 "wrongpassword"
And 我点击登录按钮
Then 我应该看到错误消息 "密码错误"
配置behat.yml
behat.yml
default:
autoload:
'': '%paths.base%/features/bootstrap'
suites:
default:
paths:
- '%paths.base%/features'
contexts:
- FeatureContext
extensions:
Behat\MinkExtension:
base_url: http://localhost
browser_name: chrome
sessions:
default:
selenium2:
wd_host: http://localhost:9515
编写FeatureContext
features/bootstrap/FeatureContext.php
<?php
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\TableNode;
use Behat\MinkExtension\Context\MinkContext;
class FeatureContext extends MinkContext implements Context
{
private array $users = [];
private string $currentUser = '';
public function __construct()
{
// 初始化测试数据
$this->users = [
'john@example.com' => [
'password' => 'password123',
'name' => 'John'
]
];
}
/**
* @Given 我在登录页面
*/
public function iAmOnLoginPage()
{
$this->visit('/login');
}
/**
* @When 我输入用户名 :username
*/
public function iEnterUsername($username)
{
$this->fillField('email', $username);
$this->currentUser = $username;
}
/**
* @When 我输入密码 :password
*/
public function iEnterPassword($password)
{
$this->fillField('password', $password);
}
/**
* @When 我点击登录按钮
*/
public function iClickLoginButton()
{
$this->pressButton('login');
}
/**
* @Then 我应该看到欢迎消息 :message
*/
public function iShouldSeeWelcomeMessage($message)
{
$this->assertPageContainsText($message);
}
/**
* @Then 我应该被重定向到仪表盘
*/
public function iShouldBeRedirectedToDashboard()
{
$this->assertPageAddress('/dashboard');
}
/**
* @Then 我应该看到错误消息 :message
*/
public function iShouldSeeErrorMessage($message)
{
$this->assertPageContainsText($message);
}
/**
* @Given 存在以下用户
*/
public function thereAreUsers(TableNode $table)
{
foreach ($table as $row) {
$this->users[$row['email']] = [
'password' => $row['password'],
'name' => $row['name']
];
}
}
}
运行Behat测试
# 运行所有测试 vendor/bin/behat # 运行指定文件 vendor/bin/behat features/login.feature # 运行指定场景 vendor/bin/behat features/login.feature:8 # 带格式化输出 vendor/bin/behat --format=pretty # 严格模式 vendor/bin/behat --strict
使用数据表
features/products.feature
Feature: 产品管理
作为管理员
我需要管理产品
以便更新商城信息
Scenario: 添加多个产品
Given 管理员已登录
When 我添加以下产品:
| 名称 | 价格 | 库存 |
| 苹果 | 5.00 | 100 |
| 香蕉 | 3.50 | 200 |
| 橙子 | 4.00 | 150 |
Then 产品列表应该包含 3 个产品
ProductContext.php
<?php
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\TableNode;
class ProductContext implements Context
{
private array $products = [];
/**
* @When 我添加以下产品:
*/
public function iAddProducts(TableNode $table)
{
foreach ($table as $row) {
$this->products[] = [
'name' => $row['名称'],
'price' => (float)$row['价格'],
'stock' => (int)$row['库存']
];
}
}
/**
* @Then 产品列表应该包含 :count 个产品
*/
public function productListShouldContain($count)
{
$actualCount = count($this->products);
if ($actualCount !== (int)$count) {
throw new \Exception("Expected $count products, got $actualCount");
}
}
}
使用正则表达式
/**
* @When /^我输入(用户名|密码) "([^"]*)"$/
*/
public function iEnterField($field, $value)
{
if ($field === '用户名') {
$this->fillField('email', $value);
} else {
$this->fillField('password', $value);
}
}
高级特性
场景大纲(Scenario Outline)
Feature: 计算器
作为用户
我需要使用计算器
以便进行数学运算
Scenario Outline: 加法运算
Given 我输入 <num1>
And 我输入 <num2>
When 我执行加法
Then 结果应该是 <result>
Examples:
| num1 | num2 | result |
| 1 | 2 | 3 |
| 10 | 20 | 30 |
| 5 | 7 | 12 |
后台(Background)
Feature: 用户管理
Background:
Given 管理员已登录
And 存在以下用户:
| name | email |
| John | john@test.com |
| Jane | jane@test.com |
Scenario: 查看用户列表
When 我访问用户列表
Then 我应该看到 2 个用户
标签(Tags)
Feature: 复杂功能
@smoke @critical
Scenario: 关键功能测试
Given 基础配置
And 额外配置
@wip @slow
Scenario: 开发中功能
Given 开发中的功能
运行带标签的测试:
vendor/bin/behat --tags=@smoke vendor/bin/behat --tags="@wip&&~@slow"
最佳实践
- 使用清晰的语言:Feature文件应使用业务语言,避免技术细节
- 保持步骤简洁:每个步骤只做一件事
- 使用数据驱动:利用Scenario Outline减少重复
- 维护测试数据:使用Fixture或Factory模式
- 合理的目录结构:按功能模块组织Feature文件
features/
├── authentication/
│ ├── login.feature
│ └── registration.feature
├── products/
│ ├── create.feature
│ ├── edit.feature
│ └── delete.feature
└── bootstrap/
└── FeatureContext.php
与CI/CD集成
GitHub Actions (.github/workflows/behat.yml)
name: Behat Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: '8.0'
- run: composer install
- run: vendor/bin/behat
Behat提供了一套强大的BDD测试框架,主要优势:
- 业务人员和开发人员可以共享相同语言
- 自动生成文档
- 易于维护
- 强类型断言
Behat不只是测试工具,更是沟通工具,能帮助团队更好地理解需求。