ssbehat / behat-quicksetup

快速设置SilverStripe框架扩展以用于Behat

1.1.0 2016-02-26 02:09 UTC

This package is not auto-updated.

Last update: 2024-09-14 18:53:01 UTC


README

Build Status

概述

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上,您也可以使用Homebrewbrew 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_manymany_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"

快捷方式

这是一个手动分类的可用命令列表,当同时安装了cmsframework模块时。它基于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”。

有用资源