ssbehat / behat-quicksetup
快速设置SilverStripe框架扩展以用于Behat
Requires
- php: >=5.3.2
- behat/behat: 2.5.*@stable
- behat/mink: 1.6.*@stable
- behat/mink-extension: 1.3.*-dev
- behat/mink-selenium2-driver: 1.2.*@stable
- chillu/fakedatabase: *
- phpunit/phpunit: 3.7.*
- silverstripe/framework: >=3.1.0
- silverstripe/testsession: *
- symfony/config: ~2.0
- symfony/console: ~2.0
- symfony/dependency-injection: ~2.0
- symfony/dom-crawler: ~2.0
- symfony/event-dispatcher: ~2.0
- symfony/finder: ~2.0
- symfony/translation: ~2.0
- symfony/yaml: ~2.0
This package is not auto-updated.
Last update: 2024-09-14 18:53:01 UTC
README
概述
Behat 是一种行为驱动开发(BDD)的测试框架。因为它主要通过浏览器与您的网站交互,所以您不需要任何特定的集成工具就可以将其与基本SilverStripe网站结合使用,只需遵循标准Behat使用说明。
如果您想超越与现有网站和数据库的交互,例如更改数据库内容(稍后需要回滚到“干净状态”),这个扩展程序就很有用。
它提供以下助手
- 提供对Behat上下文中SilverStripe类的访问
- 自动设置临时数据库
- 在每个场景上重置数据库内容
- 为SilverStripe的登录表单和其他常见任务预构建上下文
- 创建具有预定义权限的成员固定数据
- 在Behat场景中包含YML固定数据定义
- 等待jQuery Ajax响应(而不是固定的等待时间器)
- 捕获JavaScript错误并通过Selenium记录日志
- 检测到断言错误时保存屏幕截图到文件系统
为了实现这一点,该扩展程序做出了一个基本假设:您的Behat测试与测试的SilverStripe代码库相同的应用程序运行,在本地托管网站上,使用相同的代码库。这很重要,因为我们需要访问底层的SilverStripe PHP类。当然,您可以使用远程浏览器进行实际测试。
注意:此扩展程序仅与selenium2
Mink驱动程序进行了测试。
安装
简单通过Composer安装SilverStripe。通过Composer安装SilverStripe。如果要将模块添加到现有项目中,请跳过此步骤。
composer create-project silverstripe/installer my-test-project 3.1.x-dev
切换到新创建的webroot,并添加SilverStripe Behat扩展。
cd my-test-project
composer require "ssbehat/behat-quicksetup:*"
运行以下Shell脚本以帮助您设置Behat测试会话环境。
sh vendor/ssbehat/behat-quicksetup/appendixes/SS-behat-quick-setup.sh
现在获取最新的Selenium2服务器(需要Java)
wget http://selenium-release.storage.googleapis.com/2.44/selenium-server-standalone-2.44.0.jar
在OSX上,您也可以使用Homebrew:brew install selenium-server-standalone
。如果您在运行Selenium与浏览器时遇到问题,请检查您是否使用了最新驱动程序,因为上面的下载链接可能已经过时。
确保您已安装了受支持的firefox版本
wget https://ftp.mozilla.org/pub/mozilla.org/firefox/releases/26.0/mac/en-GB/Firefox%2026.0.dmg
现在通过在浏览器中打开它并遵循说明以通常的方式安装SilverStripe项目。提示:您可以通过在webroot一级以上的全局_ss_environment.php
文件中使用[SS_DATABASE_CHOOSE_NAME]
来跳过此步骤。
除非您已设置$_FILE_TO_URL_MAPPING
,否则您还需要指定webroot的URL。您可以将它添加到项目根目录中现有的behat.yml
配置文件,或者在终端会话中将其设置为环境变量
export BEHAT_PARAMS="extensions[SilverStripe\BehatExtension\MinkExtension][base_url]=https:///"
用法
启动Selenium服务器
您可以在单独的终端会话中本地运行服务器
java -jar selenium-server-standalone-2.41.0.jar
在某些情况下可能需要启动特定的firefox版本
java -jar selenium-server-standalone-2.41.0.jar -Dwebdriver.firefox.bin="/Applications/Firefox26.app/Contents/MacOS/firefox-bin"
运行测试
现在您可以运行测试(例如针对 框架
模块)
vendor/bin/behat @framework
要仅运行特定测试,请使用它们的特征文件名
vendor/bin/behat @framework/login.feature
这将默认启动 Firefox 浏览器。其他浏览器和配置文件可以在 behat.yml
中配置。
例如,如果您想启动 Chrome 浏览器,可以遵循提供的说明这里。
教程
配置
SilverStripe 安装程序已经自带了 YML 配置,它可以在本地托管的 Selenium 服务器上运行测试,位于项目根目录下的 behat.yml
。
您需要至少自定义 base_url
设置,以匹配测试的 SilverStripe 实例托管的本地 URL。这
通用的 Mink 配置设置位于 SilverStripe\BehatExtension\MinkExtension
,它是 Behat\MinkExtension\Extension
的子类。
设置概述(所有在 extensions.SilverStripe\BehatExtension\Extension
路径下)
framework_path
:SilverStripe 框架文件夹的路径。它支持绝对路径和相对于behat.yml
文件的相对路径。extensions.Behat\MinkExtension\Extension.base_url
:您可能需要更改测试过程中使用的基准 URL。它用于您在特征描述中使用相对 URL 的每次。它还将在SilverStripeExtension
中的 文件到 URL 映射 中使用。extensions.Behat\MinkExtension\Extension.files_path
:更改以支持测试中的文件上传。目前仅支持绝对路径。ajax_steps
:由于 SilverStripe 广泛使用 AJAX 请求,我们必须发明一种更高效、更简洁的方式来处理它们,而不是仅仅使用可选的ajax_steps
来匹配步骤定义,以便它们可以被 特殊 AJAX 处理程序“捕获”,这些处理程序会调整延迟。您可以使用管道分隔的字符串或匹配步骤定义的子字符串列表。ajax_timeout
:在被视为超时之前经过的毫秒数,然后脚本继续其断言以避免死锁(默认:5000)。screenshot_path
:用于存储失败步骤最后已知状态的屏幕截图的绝对路径。该目录中的屏幕截图名称由特征文件名和失败的行号组成。
示例:behat.yml
default:
context:
class: SilverStripe\MyModule\Test\Behaviour\FeatureContext
extensions:
SilverStripe\BehatExtension\Extension:
screenshot_path: %behat.paths.base%/artifacts/screenshots
SilverStripe\BehatExtension\MinkExtension:
# Adjust this to your local environment
base_url: https:///
default_session: selenium2
javascript_session: selenium2
selenium2:
browser: firefox
模块初始化
现在您可以开始编写功能了!只需在代码库的任何位置创建 *.feature
文件,并像上面那样运行它们。我们建议使用 tests/behat/features
的文件夹结构,因为它与 SilverStripe 的 PHPUnit 测试的常见位置一致。
Behat 测试依赖于包含步骤定义的 FeatureContext
类,并且可以由其他子上下文组成,例如用于 SilverStripe 特定 CMS 步骤(有关 behat.org)的细节)。由于步骤定义非常特定于领域,您很可能需要自己的上下文。SilverStripe Behat 扩展提供了一个初始化脚本,该脚本在推荐的文件夹结构中生成模板
vendor/bin/behat --init @mymodule
现在您将在 mymodule/tests/behat/features/bootstrap/Context/FeatureContext.php
中有一个类,以及一个具有 mymodule/tests/behat/features
的功能文件夹。该类是命名空间化的,默认为模块名称。您可以自定义此
vendor/bin/behat --namespace='MyVendor\MyModule' --init @mymodule
在这种情况下,您需要在运行功能时传递命名空间(至少直到 SilverStripe 模块允许声明命名空间)。
vendor/bin/behat --namespace='MyVendor\MyModule' @mymodule
可用的步骤定义
该扩展包含多个BehatContext
子类,并提供了一些额外的步骤定义。其中一些在一般网站测试中很有帮助,而其他的则是针对SilverStripe的。要找出所有可用的步骤(以及它们定义的文件),请运行以下命令:
vendor/bin/behat @mymodule --definitions=i
注意:在SilverStripe的框架
模块中还有更多针对与CMS界面交互的特定步骤定义(见framework/tests/behat/features/bootstrap
)。除了动态列表外,本指南末尾还可以找到可用的步骤速查表。
固定数据
由于每次测试运行都会创建一个新的数据库,除非您明确定义,否则您不能依赖现有状态。
数据库默认值
获取默认数据的最简单方法是使用DataObject->requireDefaultRecords()
。许多模块已经定义了此方法,例如,blog
模块会在页面树中自动创建一个默认的BlogHolder
条目。有时这些默认值可能适得其反,因此您需要通过在功能定义顶部放置@database-defaults
标签来“选择”它们。默认值在每个场景之后会自动重置。
内联定义
如果您需要更多关于正在创建哪些记录的灵活性和透明度,请使用内联定义语法。以下示例显示了某些语法变体:
Feature: Do something with pages
As an site owner
I want to manage pages
Background:
# Creates a new page without data. Can be accessed later under this identifier
Given a "page" "Page 1"
# Uses a custom RegistrationPage type
And an "error page" "Register"
# Creates a page with inline properties
And a "page" "Page 2" with "URLSegment"="page-1" and "Content"="my page 1"
# Field names can be tabular, and based on DataObject::$field_labels
And the "page" "Page 3" has the following data
| Content | <blink> |
| My Property | foo |
| My Boolean | bar |
# Pages are published by default, can be explicitly unpublished
And the "page" "Page 1" is not published
# Create a hierarchy, and reference a record created earlier
And the "page" "Page 1.1" is a child of a "page" "Page 1"
# Specific page type step
And a "page" "My Redirect" which redirects to a "page" "Page 1"
And a "member" "Website User" with "FavouritePage"="=>Page.Page 1"
@javascript
Scenario: View a page in the tree
Given I am logged in with "ADMIN" permissions
And I go to "/admin/pages"
Then I should see "Page 1" in CMS Tree
- 固定数据是在您定义的位置创建的。如果您想在每个场景之前创建固定数据,请在背景中定义它们。如果您只想在特定场景运行时创建它们,请在那里定义。
- 固定数据在场景之间会被清除。
- 基本语法适用于所有
DataObject
子类,但某些特定符号(如“未发布”)需要将扩展(如Hierarchy
)应用到类中。 - 记录类型、标识符、属性名称和属性值需要引用
- 记录类型(类名)可以使用更自然的符号(“注册页面”而不是“Registration Page”)
- 记录类型支持
$singular_name
符号,该符号也用于在CMS中引用类型。记录属性名称以相同的方式支持$field_labels
符号。 - 属性值也可以使用
=>
符号来表示记录之间的关系。符号是=><classname>.<identifier>
。对于has_many
或many_many
关系,多个关系可以通过逗号分隔。
编写Behat测试
目录结构
按照惯例,SilverStripe Behat测试位于模块的tests/behat
子文件夹中。您可以使用以下命令创建它:
mkdir -p mymodule/tests/behat/features/bootstrap/MyModule/Test/Behaviour
FeatureContext
通用的Behat使用说明也适用于此处。唯一的重大区别是扩展您的自己的FeatureContext
的基类:应该是SilverStripeContext
而不是BehatContext
。
示例:mymodule/tests/behat/features/bootstrap/MyModule/Test/Behaviour/FeatureContext.php
<?php
namespace MyModule\Test\Behaviour;
use SilverStripe\BehatExtension\Context\SilverStripeContext,
SilverStripe\BehatExtension\Context\BasicContext,
SilverStripe\BehatExtension\Context\LoginContext;
require_once 'PHPUnit/Autoload.php';
require_once 'PHPUnit/Framework/Assert/Functions.php';
class FeatureContext extends SilverStripeContext
{
public function __construct(array $parameters)
{
$this->useContext('BasicContext', new BasicContext($parameters));
$this->useContext('LoginContext', new LoginContext($parameters));
parent::__construct($parameters);
}
}
屏幕大小
在某些Selenium驱动程序(如SauceLabs)中,您可以通过capabilities
定义来定义所需的浏览器窗口大小。但是,Selenium默认不支持此功能,因此我们通过环境变量添加了一个解决方案。
BEHAT_SCREEN_SIZE=320x600 vendor/bin/behat
检查PHP会话
Behat从CLI执行,这反过来又会在浏览器中触发Web请求。此浏览器会话关联的PHP会话信息(如登录用户)。在每次请求之后,会话信息会作为TestSessionEnvironment
的一部分保存在磁盘上,以便与Behat CLI共享。
示例:检索当前登录的成员
$env = Injector::inst()->get('TestSessionEnvironment');
$state = $env->getState();
if(isset($state->session['loggedInAs'])) {
$member = \Member::get()->byID($state->session['loggedInAs']);
} else {
$member = null;
}
常见问题解答
找不到FeatureContext
这很可能是Composer自动加载生成器的问题。请检查你的vendor/composer/autoload_classmap.php
文件中是否提到了"SilverStripe",如果没有,请执行composer dump-autoload
命令。
为什么模块需要知道文件系统上的框架路径呢?
有时SilverStripe需要知道你网站的URL。当你使用浏览器访问网站时,这很容易确定,但如果你在命令行中执行脚本,它就无法知道。
为此,这个模块使用了文件到URL映射。
该模块如何与SS数据库交互?
模块在初始化时创建临时数据库,并在每个场景之前通过使用/dev/tests/setdb
TestRunner端点切换到备用数据库会话。
如果需要,它还会用默认记录填充这个临时数据库。
可以包含你自己的固定数据,下面会进一步解释。
为什么在全新安装中测试通过,但在我自己的项目中失败?
因为我们直接测试接口,任何对查看元素的更改都可能破坏测试。通过从头开始构建测试数据库,我们试图最小化这种影响。尽管如此,仍然可能出错的一些例子
- 安装默认数据的第三方SilverStripe模块
- 默认界面语言的更改
- 删除管理区域或特定字段的配置
目前还没有方法排除测试运行中的受影响模块。你必须调整测试以适应这些更改,或者在没有这些模块的“沙盒”项目中运行测试。
当出现问题时如何调试?
首先,阅读控制台输出。Behat会告诉你哪些步骤失败了。
SilverStripe行为测试框架还会通知你一些事件。它试图捕获一些JavaScript错误和AJAX错误,尽管它仅限于页面加载后发生的错误。
每次步骤标记为失败时,模块都会捕获屏幕截图。请参考上面的配置部分了解如何设置截图路径。
如果你无法使用上述方法收集的信息进行调试,可以通过添加以下步骤来延迟步骤执行
And I put a breakpoint
这将停止测试执行,直到你在终端中按下回车键。当你想在浏览器中查看错误或开发者控制台,或者想手动与会话页面交互时,这非常有用。
我可以通过XDebug设置断点吗?
如果你设置了XDebug,断点将是你的朋友。问题是,你只能将调试器连接到CLI中的PHP执行,或者连接到浏览器,但不能同时连接到两者。
首先,确保xdebug.remote_autostart
设置为Off
,否则你将在CLI中始终有一个活动的调试会话,永远不会在浏览器中。
然后你可以选择为当前的CLI运行启用XDebug
XDEBUG_CONFIG="idekey=macgdbp" vendor/bin/behat
或者,你可以使用TESTSESSION_PARAMS
环境变量将额外的参数传递给dev/testsession/start
,并在浏览器中进行调试。
TESTSESSION_PARAMS="XDEBUG_SESSION_START=macgdbp" vendor/bin/behat @app
macgdbp
IDE密钥需要与你的php.ini
中的xdebug.idekey
设置匹配。
我如何使用SauceLabs.com进行远程Selenium2测试?
以下是为你的behat.yml
的示例配置文件
# Saucelabs.com sample setup, use with "vendor/bin/behat --profile saucelabs"
saucelabs:
extensions:
SilverStripe\BehatExtension\MinkExtension:
selenium2:
browser: firefox
# Add your own username and API token here
wd_host: <user>:<api-token>@ondemand.saucelabs.com/wd/hub
capabilities:
platform: "Windows 2008"
browser: "firefox"
version: "15"
快捷方式
这是一个手动分类的可用命令列表,当同时安装了cms
和framework
模块时。它基于vendor/bin/behat -di @cms
的输出。
基础
Then /^(?:|I )should see "(?P<text>(?:[^"]|\\")*)"$/
- Checks, that page contains specified text.
Then /^(?:|I )should not see "(?P<text>(?:[^"]|\\")*)"$/
- Checks, that page doesn't contain specified text.
Then /^(?:|I )should see text matching (?P<pattern>"(?:[^"]|\\")*")$/
- Checks, that page contains text matching specified pattern.
Then /^(?:|I )should not see text matching (?P<pattern>"(?:[^"]|\\")*")$/
- Checks, that page doesn't contain text matching specified pattern.
Then /^the response should contain "(?P<text>(?:[^"]|\\")*)"$/
- Checks, that HTML response contains specified string.
Then /^the response should not contain "(?P<text>(?:[^"]|\\")*)"$/
- Checks, that HTML response doesn't contain specified string.
Then /^(?:|I )should see "(?P<text>(?:[^"]|\\")*)" in the "(?P<element>[^"]*)" element$/
- Checks, that element with specified CSS contains specified text.
Then /^(?:|I )should not see "(?P<text>(?:[^"]|\\")*)" in the "(?P<element>[^"]*)" element$/
- Checks, that element with specified CSS doesn't contain specified text.
Then /^the "(?P<element>[^"]*)" element should contain "(?P<value>(?:[^"]|\\")*)"$/
- Checks, that element with specified CSS contains specified HTML.
Then /^(?:|I )should see an? "(?P<element>[^"]*)" element$/
- Checks, that element with specified CSS exists on page.
Then /^(?:|I )should not see an? "(?P<element>[^"]*)" element$/
- Checks, that element with specified CSS doesn't exist on page.
Then /^(?:|I )should be on "(?P<page>[^"]+)"$/
- Checks, that current page PATH is equal to specified.
Then /^the (?i)url(?-i) should match (?P<pattern>"([^"]|\\")*")$/
- Checks, that current page PATH matches regular expression.
Then /^the response status code should be (?P<code>\d+)$/
- Checks, that current page response status is equal to specified.
Then /^the response status code should not be (?P<code>\d+)$/
- Checks, that current page response status is not equal to specified.
Then /^(?:|I )should see (?P<num>\d+) "(?P<element>[^"]*)" elements?$/
- Checks, that (?P<num>\d+) CSS elements exist on the page
Then /^print last response$/
- Prints last response to console.
Then /^show last response$/
- Opens last response content in browser.
Then /^I should be redirected to "([^"]+)"/
Given /^I wait (?:for )?([\d\.]+) second(?:s?)$/
Then /^the "([^"]*)" table should contain "([^"]*)"$/
Then /^the "([^"]*)" table should not contain "([^"]*)"$/
Given /^I click on "([^"]*)" in the "([^"]*)" table$/
导航
Given /^(?:|I )am on homepage$/
- Opens homepage.
When /^(?:|I )go to homepage$/
- Opens homepage.
Given /^(?:|I )am on "(?P<page>[^"]+)"$/
- Opens specified page.
When /^(?:|I )go to "(?P<page>[^"]+)"$/
- Opens specified page.
When /^(?:|I )reload the page$/
- Reloads current page.
When /^(?:|I )move backward one page$/
- Moves backward one page in history.
When /^(?:|I )move forward one page$/
- Moves forward one page in history
表单
When /^(?:|I )press "(?P<button>(?:[^"]|\\")*)"$/
- Presses button with specified id|name|title|alt|value.
When /^(?:|I )follow "(?P<link>(?:[^"]|\\")*)"$/
- Clicks link with specified id|title|alt|text.
When /^(?:|I )fill in "(?P<field>(?:[^"]|\\")*)" with "(?P<value>(?:[^"]|\\")*)"$/
- Fills in form field with specified id|name|label|value.
When /^(?:|I )fill in "(?P<value>(?:[^"]|\\")*)" for "(?P<field>(?:[^"]|\\")*)"$/
- Fills in form field with specified id|name|label|value.
When /^(?:|I )fill in the following:$/
- Fills in form fields with provided table.
When /^(?:|I )select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)"$/
- Selects option in select field with specified id|name|label|value.
When /^(?:|I )additionally select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)"$/
- Selects additional option in select field with specified id|name|label|value.
When /^I select the "([^"]*)" radio button$/
- Selects a radio button with the given id|name|label|value
When /^(?:|I )check "(?P<option>(?:[^"]|\\")*)"$/
- Checks checkbox with specified id|name|label|value.
When /^(?:|I )uncheck "(?P<option>(?:[^"]|\\")*)"$/
- Unchecks checkbox with specified id|name|label|value.
When /^(?:|I )attach the file "(?P[^"]*)" to "(?P<field>(?:[^"]|\\")*)"$/
- Attaches file to field with specified id|name|label|value.
Then /^the "(?P<field>(?:[^"]|\\")*)" field should contain "(?P<value>(?:[^"]|\\")*)"$/
- Checks, that form field with specified id|name|label|value has specified value.
Then /^the "(?P<field>(?:[^"]|\\")*)" field should not contain "(?P<value>(?:[^"]|\\")*)"$/
- Checks, that form field with specified id|name|label|value doesn't have specified value.
Then /^the "(?P<checkbox>(?:[^"]|\\")*)" checkbox should be checked$/
- Checks, that checkbox with specified in|name|label|value is checked.
Then /^the "(?P<checkbox>(?:[^"]|\\")*)" checkbox should not be checked$/
- Checks, that checkbox with specified in|name|label|value is unchecked.
Given /^(?:|I )attach the file "(?P[^"]*)" to "(?P<field>(?:[^"]|\\")*)" with HTML5$/
When /^I fill in the "(?P<field>([^"]*))" HTML field with "(?P<value>([^"]*))"$/
When /^I fill in "(?P<value>([^"]*))" for the "(?P<field>([^"]*))" HTML field$/
When /^I append "(?P<value>([^"]*))" to the "(?P<field>([^"]*))" HTML field$/
Then /^the "(?P<locator>([^"]*))" HTML field should contain "(?P<html>([^"]*))"$/
When /^(?:|I )fill in the "(?P<field>(?:[^"]|\\")*)" dropdown with "(?P<value>(?:[^"]|\\")*)"$/
- Workaround for chosen.js dropdowns or tree dropdowns which hide the original dropdown field.
When /^(?:|I )fill in "(?P<value>(?:[^"]|\\")*)" for "(?P<field>(?:[^"]|\\")*)" dropdown$/
- Workaround for chosen.js dropdowns or tree dropdowns which hide the original dropdown field.
Given /^I select "([^"]*)" from "([^"]*)" input group$/
- Check an individual input button from a group of inputs
- Example: I select "Admins" from "Groups" input group
(where "Groups" is the title of the CheckboxSetField or OptionsetField form field)
交互
Given /^I press the "([^"]*)" button$/
Given /^I click "([^"]*)" in the "([^"]*)" element$/
Given /^I type "([^"]*)" into the dialog$/
Given /^I (?:press|follow) the "([^"]*)" (?:button|link), confirming the dialog$/
Given /^I (?:press|follow) the "([^"]*)" (?:button|link), dismissing the dialog$/
Given /^I confirm the dialog$/
Given /^I dismiss the dialog$/
登录
Given /^I am logged in with "([^"]*)" permissions$/
- Creates a member in a group with the correct permissions.
Given /^I am not logged in$/
When /^I log in with "(?<username>[^"]*)" and "(?<password>[^"]*)"$/
Given /^I should see a log-in form$/
Then /^I will see a "bad" log-in message$/
CMS UI
Then /^I should see an edit page form$/
Then /^I should see the CMS$/
Then /^I should see a "([^"]*)" notice$/
Then /^I should see a "([^"]*)" message$/
Given /^I should see a "([^"]*)" button in CMS Content Toolbar$/
When /^I should see "([^"]*)" in CMS Tree$/
When /^I should not see "([^"]*)" in CMS Tree$/
When /^I expand the "([^"]*)" CMS Panel$/
When /^I click the "([^"]*)" CMS tab$/
Then /^I can see the preview panel$/
Given /^the preview contains "([^"]*)"$/
Given /^the preview does not contain "([^"]*)"$/
固定数据
Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" (:?which )?redirects to (?:(an|a|the) )"(?<targetType>[^"]+)" "(?<targetId>[^"]+)"$/
- Find or create a redirector page and link to another existing page.
Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)"$/
- Example: Given a "page" "Page 1"
Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" with (?<data>.*)$/
- Example: Given a "page" "Page 1" with "URL"="page-1" and "Content"="my page 1"
Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" has the following data$/
- Example: And the "page" "Page 2" has the following data
Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" is a (?<relation>[^\s]*) of (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)"/
- Example: Given the "page" "Page 1.1" is a child of the "page" "Page1"
Note that this change is not published by default
Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" is (?<state>[^"]*)$/
- Example: Given the "page" "Page 1" is not published
- Example: Given the "page" "Page 1" is published
- Example: Given the "page" "Page 1" is deleted
Given /^there are the following ([^\s]*) records$/
- Accepts YAML fixture definitions similar to the ones used in SilverStripe unit testing.
Given /^(?:(an|a|the) )"member" "(?<id>[^"]+)" belonging to "(?<groupId>[^"]+)"$/
- Example: Given a "member" "Admin" belonging to "Admin Group"
Given /^(?:(an|a|the) )"member" "(?<id>[^"]+)" belonging to "(?<groupId>[^"]+)" with (?<data>.*)$/
Given /^(?:(an|a|the) )"group" "(?<id>[^"]+)" (?:(with|has)) permissions (?<permissionStr>.*)$/
- Example: Given a "group" "Admin" with permissions "Access to 'Pages' section" and "Access to 'Files' section"
Given /^I assign (?:(an|a|the) )"(?<type>[^"]+)" "(?<value>[^"]+)" to (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)"$/
- Example: I assign the "TaxonomyTerm" "For customers" to the "Page" "Page1"
Given /^I assign (?:(an|a|the) )"(?<type>[^"]+)" "(?<value>[^"]+)" to (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)" in the "(?<relationName>[^"]+)" relation$
- Example: I assign the "TaxonomyTerm" "For customers" to the "Page" "Page1" in the "Terms" relation
Given /^the CMS settings have the following data$/
- Example: Given the CMS settings has the following data
- Note: It only works with the SilverStripe CMS module installed
环境
Given /^the current date is "([^"]*)"$/
Given /^the current time is "([^"]*)"$/
电子邮件
Given /^there should (not |)be an email (to|from) "([^"]*)"$/
Given /^there should (not |)be an email (to|from) "([^"]*)" titled "([^"]*)"$/
Given /^the email should (not |)contain "([^"]*)"$/
- Example: Given the email should contain "Thank you for registering!"
When /^I click on the "([^"]*)" link in the email (to|from) "([^"]*)"$/
When /^I click on the "([^"]*)" link in the email (to|from) "([^"]*)" titled "([^"]*)"$/
When /^I click on the "([^"]*)" link in the email"$/
Given /^I clear all emails$/
Then /^the email should (not |)contain the following data:$/
Example: Then the email should contain the following data:
转换
Behat 转换可以根据它们的原始值更改步骤参数,例如将匹配\d
正则表达式的任何参数转换为实际的PHP整数。
/^(?:(the|a)) time of (?<val>.*)$/
:转换与strtotime()兼容的相对时间语句。例如:“1小时前的时间”如果当前时间是“23:00:00”,可能会返回“22:00:00”。/^(?:(the|a)) date of (?<val>.*)$/
:转换与strtotime()兼容的相对日期语句。例如:“2天前的日期”如果当前是2013年10月12日,可能会返回“2013-10-10”。/^(?:(the|a)) datetime of (?<val>.*)$/
:转换与strtotime()兼容的相对日期和时间语句。例如:“2天前的日期和时间”如果当前是2013年10月12日,可能会返回“2013-10-10 23:00:00”。