行为驱动开发是为代码编写测试的好方法,因为它使用的语言是真人可以理解的。一旦您了解 BDD 及其好处,您可能希望在您的下一个项目中实施它。让我们看看如何在 Drupal 中使用 Behat 和 Mink 扩展来实施 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 是一种人类可读的格式,遵循上下文-操作-结果模式。它由几个特殊的关键字组成,这些关键字在解析后将执行命令,以模拟用户与网站的交互。
以关键字 Given、When 和 Then 开头的句子分别表示上下文、操作和结果。它们被称为步骤,应该从执行操作的用户的角度编写。Behat 将读取它们并执行相应的步骤定义。(稍后详细介绍。)
此示例指示浏览器访问“contact/feedback”链接下的页面,填写一些字段值,按下一个按钮,并检查页面上是否存在消息以验证操作是否有效。运行测试;您的输出应类似于这样
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 Extension 提供的一些步骤定义。在与 Features 文件夹相同的级别(而不是在其中)创建一个 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: ~
步骤定义通过上下文提供。当您初始化 Behat 时,它创建了一个没有步骤定义的 FeatureContext。在上面的示例中,我们正在更新配置文件,以包含此空上下文以及 Drupal Behat Extension 提供的其他上下文。再次运行 ./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 name 属性来思考。这就是为什么使用 subject[0][value] 和 message[0][value] 编写测试既晦涩难懂,又不是很用户友好。您可以通过在 features/bootstrap/FeatureContext.php 中创建自定义步骤来改进这一点,这是在 Behat 初始化时生成的。
此外,如果您多次运行测试,您会发现它开始失败。这是因为 Drupal 默认情况下对每小时提交次数限制为五次。每次运行测试时,就像真实用户在执行操作一样。一旦达到限制,您将在 Drupal 界面上收到错误。测试失败是因为缺少预期的成功消息。
这说明了调试测试的重要性。有一些步骤可以帮助解决这个问题,例如 Then print last drush output 和 Then I break。更好的是使用真正的调试器,例如 Xdebug。您还可以安装其他软件包,这些软件包提供更多专门用于调试目的的步骤定义,例如 Behatch 和 Nuvole's 扩展。例如,您可以配置 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 Extension 使用可以执行 Drupal 特有操作的驱动程序;例如,为您的测试创建用户和节点。虽然您可以按照注册过程创建用户并分配角色,但更简单的方法是简单地使用类似 Given I am logged in as a user with the "Authenticated user" role 的步骤。为了使此操作生效,您需要 指定您是要使用 Drupal 驱动程序还是 Drush 驱动程序。确保相应地更新您的 behat.yml 文件。例如,要使用 Drupal 驱动程序
default:
extensions:
Drupal\DrupalExtension:
blackbox: ~
api_driver: drupal
drupal:
drupal_root: ./relative/path/to/drupal
我希望这个 Drupal 中 BDD 测试的介绍对您有所帮助。如果您有任何问题,请随时在下面添加评论,发送电子邮件至 {我的名字}@{我的姓氏}.me 或在 Twitter 上发推文 @dinarcon。
Mauricio Dinarte 将在 2019 年 4 月 8-12 日在西雅图 DrupalCon 上展示 Drupal 8 中使用 Behat 的行为驱动开发。
评论已关闭。