行为驱动的开发是编写代码测试的好方法,因为它使用的是真实人类可以理解的语言。 了解BDD及其好处之后 ,您可能希望在下一个项目中实现它。 让我们看看如何使用带有Mink扩展的Behat在Drupal中实现BDD。
由于使用Composer来管理Drupal网站的依赖关系是一种好习惯,因此可以使用它来安装BDD测试工具:Behat,Mink和Behat Drupal Extension。 Behat Drupal Extension在其依赖项中列出了Behat和Mink,因此您可以通过安装Behat Drupal Extension软件包来获得所有工具:
composer require drupal/drupal-extension --dev
Mink允许您以人类可读的格式编写测试。 例如:
鉴于我是注册用户,
当我访问主页时,
然后我应该看到个性化的新闻提要
由于这些测试应该模拟用户交互,因此可以假定它们将在Web浏览器中执行。 那就是Mink发挥作用的地方。 有各种浏览器仿真器,例如Goutte和Selenium,它们的行为各不相同,并且具有非常不同的API。 Mink允许您编写一次测试,然后在不同的浏览器仿真器中执行该测试。 用外行的术语来说,Mink允许您以编程方式控制浏览器以模拟用户的操作。
既然已经安装了工具,那么应该已经有了一个behat命令。 如果运行它:
./vendor/bin/behat
您应该得到一个错误,例如:
FeatureContext context class not found and can not be used
首先初始化Behat:
./vendor/bin/behat --init
这将创建两个文件夹和一个文件,稍后我们将对其进行重新访问。 现在,在没有额外参数的情况下运行behat不会产生错误。 相反,您应该看到类似于以下的输出:
No scenarios
No steps
0m0.00s
( 7.70Mb
)
现在,您可以准备编写第一个测试,例如,以验证网站访问者是否可以使用网站范围的联系表留言。
默认情况下,Behat将在初始化项目时在创建的features文件夹中查找文件。 该文件夹中的文件应具有.feature扩展名。 让我们测试站点范围的联系表。 在features文件夹中创建一个具有以下内容的文件contact-form.feature :
Feature
: Contact form
In order to send a message to the site administrators
As a visitor
I should be able to use the site-wide contact form
Scenario
: A visitor can use the site-wide contact form
Given I am at
"contact/feedback"
When I fill in
"name" with
"John Doe"
And I fill in
"mail" with
"john@doe.com"
And I fill in
"subject" with
"Hello world"
And I fill in
"message" with
"Lorem Ipsum"
And I press
"Send message"
Then I should see the text
"Your message has been sent."
Behat测试以Gherkin编写,Gherkin是一种易于理解的格式,遵循Context-Action-Outcome模式。 它由几个特殊关键字组成,这些特殊关键字在解析后将执行命令来模拟用户与网站的交互。
以关键字Given , When和Then开头的句子分别表示上下文,动作和结果。 它们被称为“ 步骤” ,应该从用户执行操作的角度来编写它们。 Behat将阅读它们并执行相应的步骤定义 。 (稍后对此有更多介绍。)
本示例指示浏览器访问“联系/反馈”链接下的页面,填写一些字段值,按下按钮,并检查页面上是否存在消息以验证该操作是否有效。 运行测试; 您的输出应类似于以下内容:
1 scenario
(
1 undefined
)
7 steps
(
7 undefined
)
0m0.01s
( 8.01Mb
)
>> default suite has undefined steps. Please choose the context to generate snippets:
[
0
] None
[
1
] FeatureContext
>
在提示符下键入0以选择“无”选项。 这可以验证Behat找到了测试并尝试执行该测试,但是它抱怨未定义的步骤。 这些是“ 步骤定义” ,即PHP代码,将执行完成该步骤所需的任务。 您可以通过运行以下命令检查哪些步骤定义可用:
./vendor/bin/behat -dl
当前没有步骤定义,因此您不应看到任何输出。 您可以编写自己的代码,但是现在,您可以使用Mink扩展程序和Behat Drupal扩展程序提供的功能。 与功能文件夹(不在其中)创建同一级别的behat.yml文件,其内容如下:
default :
suites :
default :
contexts
:
- FeatureContext
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\MinkContext
- Drupal\DrupalExtension\Context\MessageContext
- Drupal\DrupalExtension\Context\DrushContext
extensions :
Behat\MinkExtension :
goutte
: ~
步骤定义是通过Context提供的。 初始化Behat时,它会创建一个FeatureContext,而没有任何步骤定义。 在上面的示例中,我们正在更新配置文件以包括此空上下文以及Drupal Behat扩展提供的其他上下文。 再次运行./vendor/bin/behat -dl会产生一个120多个可使用步骤的列表。 这是输出的精简版本:
default
| Given I am an anonymous user
default
| When I visit :path
default
| When I click :link
default
| Then I
( should
) see the text :text
现在,您可以执行许多操作。 使用./vendor/bin/behat再次运行测试。测试应失败,并显示类似以下错误:
Scenario: A visitor can use the site-wide contact form
# features/contact-form.feature:8
And I am at
"contact/feedback"
# Drupal\DrupalExtension\Context\MinkContext::assertAtPath()
When I fill
in
"name" with
"John Doe"
# Drupal\DrupalExtension\Context\MinkContext::fillField()
And I fill
in
"mail" with
"john@doe.com"
# Drupal\DrupalExtension\Context\MinkContext::fillField()
And I fill
in
"subject" with
"Hello world"
# Drupal\DrupalExtension\Context\MinkContext::fillField()
Form field with
id
| name
| label
| value
| placeholder
"subject" not found.
( Behat\Mink\Exception\ElementNotFoundException
)
And I fill
in
"message" with
"Lorem Ipsum"
# Drupal\DrupalExtension\Context\MinkContext::fillField()
And I press
"Send message"
# Drupal\DrupalExtension\Context\MinkContext::pressButton()
Then I should see the text
"Your message has been sent."
# Drupal\DrupalExtension\Context\MinkContext::assertTextVisible()
--- Failed scenarios:
features
/ contact-form.feature:
8
1 scenario
(
1 failed
)
7 steps
(
3 passed,
1 failed,
3 skipped
)
0m0.10s
( 12.84Mb
)
输出显示,前三个步骤(访问联系人页面并填写名称和主题字段)已完成。 但是,当用户尝试输入主题时,测试将失败,然后跳过其余步骤。 这些步骤要求您使用呈现表单字段HTML标签的name属性。
创建测试时,我故意将正确的值用于名称和地址字段,以便它们通过。 如有疑问,请使用浏览器的开发人员工具检查源代码,并找到应使用的正确值。 通过这样做,我发现我应该使用subject [0] [value]作为主题,并使用message [0] [value]作为消息。 当我更新测试以使用这些值并再次运行它时,它应该以飞色通过并产生类似于以下内容的输出:
1 scenario (1 passed)
7 steps (7 passed)
0m0.29s (12.88Mb)
成功! 测试通过! 如果您想知道,我使用的是Goutte浏览器。 它是一个命令行浏览器,与Behat一起使用的驱动程序是作为Behat Drupal Extension软件包的依赖项安装的。
如上所述,应该从用户执行操作的角度编写BDD测试。 用户不会考虑HTML名称属性。 这就是为什么使用subject [0] [value]和message [0] [value]编写测试既含糊又不太用户友好的原因。 您可以通过在behat初始化时生成的features / bootstrap / FeatureContext.php中创建自定义步骤来对此进行改进。
另外,如果您多次运行测试,您会发现它开始失败。 这是因为默认情况下,Drupal每小时限制五个提交。 每次运行测试时,就像是真实用户在执行操作。 一旦达到限制,您将在Drupal界面上收到错误消息。 测试失败,因为缺少预期的成功消息。
这说明了调试测试的重要性。 有一些步骤可以帮助您解决此问题,例如, 然后打印最后一个笔刷输出 , 然后我中断 。 最好还是使用真正的调试器,例如Xdebug 。 您还可以安装其他软件包,这些软件包提供了专门用于调试目的的更多步骤定义,例如Behatch和Nuvole的扩展。 例如,您可以将Behat配置为在测试失败时(如果您使用的驱动程序提供了此功能)对浏览器的状态进行屏幕截图。
关于驱动程序和浏览器模拟器,Goutte不支持JavaScript。 如果某个功能取决于JavaScript,则可以通过将Selenium2Driver与Geckodriver结合使用来对其进行测试 和Firefox。 每个驱动程序和浏览器都有不同的功能。 例如,Goutte驱动程序提供对响应的HTTP状态代码的访问,而Selenium2Driver不提供。 (您可以在Mink和Behat中阅读有关驱动程序的更多信息。)为了使Behat能够使用启用了javascript的驱动程序/浏览器,您需要使用@javascript标记来注释方案。 例:
Feature
:
( feature description
)
@javascript
Scenario
: An editor can select the author of a node from an autocomplete field
( list of steps
)
对于Drupal网站有用的另一个标签是@api 。 这指示Behat Drupal扩展程序使用可以执行特定于Drupal的操作的驱动程序; 例如,为您的测试创建用户和节点。 尽管您可以按照注册过程来创建用户和分配角色,但是使用“ 给定我以具有“已认证用户””角色的用户身份登录的步骤就更容易了。 为此,您需要指定是要使用Drupal还是Drush驱动程序 。 确保相应地更新您的behat.yml文件。 例如,使用Drupal驱动程序:
default :
extensions :
Drupal\DrupalExtension :
blackbox
: ~
api_driver
: drupal
drupal :
drupal_root
: ./relative/path/to/drupal
我希望在Drupal中进行BDD测试的介绍对您有帮助。 如果您有任何疑问,请随时在下面添加评论,给我发送电子邮件到{我的名字} @ {我的名字} .me或通过@dinarcon发送推文。
Mauricio Dinarte将于2019年4月8日至12日在西雅图DrupalCon上与 Behat一起介绍Drupal 8中的行为驱动开发 。