敏捷管理中的bdd_Laravel中的BDD:Behat和PhpSpec入门

敏捷管理中的bdd

This post was originally published in the community tutorials section of Semaphore CI, the hosted continuous integration and deployment service.

这篇文章最初发布Semaphore CI的社区教程部分, Semaphore CI是托管的持续集成和部署服务。



BDD (Behavior Driven Development) is a complicated subject for many developers, and getting started with it the right way often does not come easy – especially when needing to implement it into existing frameworks. This tutorial aims to help you get a BDD-powered Laravel project up and running in very little time, introducing you to the basic concepts and workflow you’ll need to proceed on your own. We’ll be installing and using Behat and PhpSpec.

对于许多开发人员而言,BDD( 行为驱动开发 )是一个复杂的主题,而以正确的方式开始使用BDD通常并不容易,尤其是在需要将其实现到现有框架中时。 本教程旨在帮助您在短时间内启动并运行由BDD驱动的Laravel项目,向您介绍您需要自己进行的基本概念和工作流程。 我们将安装和使用贝哈特PhpSpec

In the tutorial, we assume you’re working on a Unix system and have basic theoretical knowledge of what BDD is about, but little or no practical experience.

在本教程中,我们假定您正在Unix系统上工作,并且具有有关BDD的基本理论知识,但是几乎没有实践经验。

We’ll also assume that us saying “Run the command” implies the command should be run in the terminal of the operating system.

我们还将假设我们说“运行命令”意味着该命令应在操作系统的终端中运行。



先决条件 (Prerequisites)

Optionally, if you intend to build what we set up here into a proper application, add in:

(可选)如果您打算将此处设置的内容构建到适当的应用程序中,请添加:

  • a database (MySQL)

    数据库(MySQL)
  • Caching layers (Redis, Memcached…)

    缓存层(Redis,Memcached ...)

创建一个新的Laravel应用 (Creating a New Laravel App)

To create a new Laravel application, we run the following command:

要创建一个新的Laravel应用程序,我们运行以下命令:

composer create-project laravel/laravel bdd-setup

The sample application is now created, and should greet you with “Laravel 5” if you visit the root of the app.

现在创建了示例应用程序,如果您访问应用程序的根目录,则应使用“ Laravel 5”打招呼。

Laravel Greeting Screen

设置Behat (Setting up Behat)

Several packages are required in order to make Behat play well with Laravel. Let’s install them all into our application’s development environment (with the --dev flag) and explain each.

为了使Behat与Laravel兼容,需要几个软件包。 让我们将它们全部安装到应用程序的开发环境中(使用--dev标志),并分别进行说明。

composer require behat/behat behat/mink behat/mink-extension laracasts/behat-laravel-extension --dev
sudo ln -s /home/vagrant/Code/bdd-setup/vendor/bin/behat /usr/local/bin/behat

behat/behat is the main package for Behat. The behat/mink package is used to emulate a browser, so we can have the test suite check our URLs and their output. behat/mink-extension is the glue for Mink and Behat, and the last package, behat-laravel-extension is Jeffrey Way’s own implementation of Behat bindings, specifically made for Laravel.

behat/behatbehat/behat的主要软件包。 behat/mink包用于模拟浏览器,因此我们可以让测试套件检查URL及其输出。 behat/mink-extension是Mink和behat-laravel-extension ,最后一个软件包behat-laravel-extensionJeffrey Way自己实现的Behat绑定,特别是为Laravel设计的。

The last sudo ln -s line is optional, and adds Behat’s executable to a location in the $PATH, so the behat command can be executed without the vendor/bin prefix from our project’s root folder. In other words, behat --init instead of vendor/bin/behat --init.

最后一个sudo ln -s行是可选的,并将Behat的可执行文件添加到$PATH中的某个位置,因此可以执行behat命令,而无需我们项目根目录中的vendor/bin前缀。 换句话说,使用behat --init代替vendor/bin/behat --init

Finally, we initialize a Behat project:

最后,我们初始化一个Behat项目:

behat --init

This has created a new folder called features in our project’s directory:

这就在我们项目的目录中创建了一个名为features的新文件夹:

Features bootstrap folder

上下文和配置 (Context and Configuration)

Behat uses definitions from the auto-generated FeatureContext class to understand what we’re testing for – phrasing like “Given that I’m on the URL this and that…”.

Behat使用自动生成的FeatureContext类中的定义来了解我们正在测试的内容-诸如“给出我在该URL上……”的表述。

To get some typical browser-related definitions, we make sure the FeatureContext class extends the MinkContext class which contains them.

为了获得一些典型的与浏览器相关的定义,我们确保FeatureContext类扩展了包含它们的MinkContext类。

Thus, we alter the source code of FeatureContext.php from:

因此,我们从以下位置更改FeatureContext.php的源代码:

class FeatureContext implements Context, SnippetAcceptingContext

to

class FeatureContext extends Behat\MinkExtension\Context\MinkContext implements Context, SnippetAcceptingContext

With this change, we made FeatureContext inherit the definitions within MinkContext.

进行此更改后,我们使FeatureContext继承了MinkContext的定义。

The behat -dl command is used to list out all defined definitions. It will now output something like the following:

behat -dl命令用于列出所有定义的定义。 现在它将输出类似以下内容的内容:

default | Given /^(?:|I )am on (?:|the )homepage$/
default |  When /^(?:|I )go to (?:|the )homepage$/
default | Given /^(?:|I )am on "(?P<page>[^"]+)"$/
default |  When /^(?:|I )go to "(?P<page>[^"]+)"$/
default |  When /^(?:|I )reload the page$/

Next, we need to set up the Laravel specific package. As per instructions, this is done by adding a behat.yml file to the project root:

接下来,我们需要设置Laravel特定的软件包。 按照说明 ,这是通过将behat.yml文件添加到项目根目录来完成的:

default:
    extensions:
        Laracasts\Behat:
            # env_path: .env.behat
        Behat\MinkExtension:
            default_session: laravel
            base_url: http://localhost:8888
            laravel: ~

The .env.behat file referenced above contains environment variables specific to the Behat testing session. This file does not exist by default, so we can create it by copying the already included .env.example one:

.env.behat文件包含特定于Behat测试会话的环境变量。 该文件默认情况下不存在,因此我们可以通过复制已经包含的.env.example一个文件来创建它:

cp .env.example .env.behat

Note: Due to varying installation procedures between different Laravel versions, you might have to add a custom made key into APP_KEY in both config/app.php and .env, as well as .env.behat. Keeping it at under 32 characters (default is “Some random string”) will throw errors.

注:由于改变不同Laravel版本之间的安装程序,你可能必须添加定制的钥匙插入APP_KEY在这两个config/app.php.env ,以及.env.behat 。 保持少于32个字符(默认为“某些随机字符串”)会引发错误。

写作特征 (Writing Features)

Features are what we test for with Behat. We write them out as human readable stories, and expect the test suite to not only understand them, but also to make sure they work.

功能是我们使用Behat测试的功能。 我们将它们写成可读的故事,并期望测试套件不仅能够理解它们,而且还能确保它们起作用。

One such feature can be checking for whether or not we see “Laravel 5” when we visit the home page. To write this feature, we create a hometest.feature file in the features folder and give it the following contents:

其中一项功能是检查访问主页时是否看到“ Laravel 5”。 要编写此功能,我们在features文件夹中创建一个hometest.feature文件,并为其提供以下内容:

Feature:
    In order to prove that Behat works as intended
    We want to test the home page for a phrase

Every feature begins with such a description. This is for humans only – the test suite is not intended to understand this. Then follow the Scenarios – the specific, computer-readable steps the suite should follow.

每个功能都以这样的描述开头。 这仅适用于人类-测试套件并非旨在了解这一点。 然后按照方案 -套件应遵循的特定的计算机可读步骤进行操作。

Feature:
    In order to prove that Behat works as intended
    We want to test the home page for a phrase
    
    Scenario: Root Test
        When I am on the homepage
        Then I should see "Laravel 5"

Every scenario starts with the word “Scenario”, indented to the level of the Feature’s description. Every scenario should also have a name.

每个方案都以“方案”一词开头,缩进功能说明的级别。 每个方案都应有一个名称。

Immediately beneath it and another indentation level in, the scenario will have specific instructions for Behat to follow. These instructions are parsed from definitions we defined in the FeatureContext class. In our case, we defined them by extending MinkContext.

在场景的正下方和其中的另一个缩进级别中,该场景将具有供Behat遵循的特定说明。 这些指令是从我们在FeatureContext类中定义的定义中解析出来的。 在我们的例子中,我们通过扩展MinkContext定义了它们。

When I am on the homepage is a specific definition in MinkContext which states:

When I am on the homepage上时, MinkContext一个特定定义指出:

/**
     * Opens homepage.
     *
     * @Given /^(?:|I )am on (?:|the )homepage$/
     * @When /^(?:|I )go to (?:|the )homepage$/
     */
    public function iAmOnHomepage()
    {
        $this->visitPath('/');
    }

In other words, two phrases will trigger this: Given I am on the homepage and When I am on the homepage. The function will simulate a visit to the root URL: /.

换句话说,两个词组将触发此操作: Given I am on the homepageWhen I am on the homepage 。 该函数将模拟对根URL /的访问。

The next definition, Then I should see "Laravel 5" calls on:

下一个定义, Then I should see "Laravel 5"调用:

/**
     * Checks, that page contains specified text.
     *
     * @Then /^(?:|I )should see "(?P<text>(?:[^"]|\\")*)"$/
     */
    public function assertPageContainsText($text)
    {
        $this->assertSession()->pageTextContains($this->fixStepArgument($text));
    }

The function grabs all the text from the rendered page, and checks if our string is a substring of it.

该函数从呈现的页面中获取所有文本,并检查我们的字符串是否是它的子字符串。

Before testing for this, however, we need to boot up a local PHP server, just so Mink can actually access the URLs we ask it to access.

但是,在对此进行测试之前,我们需要启动本地PHP服务器,以便Mink可以实际访问我们要求其访问的URL。

php -S localhost:8888 -t public

The above command launches a server (-S), on the url localhost, listening on the port 8888 in the target directory public.

上面的命令在url localhost上启动服务器( -S ),侦听目标目录public的端口8888

Finally, we can test the feature:

最后,我们可以测试该功能:

> behat

Feature:
  In order to prove that Behat works as intended
  We want to test the home page for a phrase

  Scenario: Root Test             # features/hometest.feature:5
    When I am on the homepage     # FeatureContext::iAmOnHomepage()
    Then I should see "Laravel 5" # FeatureContext::assertPageContainsText()

1 scenario (1 passed)
2 steps (2 passed)
0m0.64s (22.13Mb)

The basics of Behat are now in place. We’ll work on some in-depth integrations in a future post.

Behat的基础知识已经到位。 在以后的文章中,我们将进行一些深入的集成。

Note: By using the behat-laravel-extension package, we made sure all Laravel functionality is instantly available in the FeatureContext. Getting to the main $app object is now as simple as app(), getting a configuration variable is just a config("somevar") away. These bindings are all automatically available and ready to be used.

注意 :通过使用behat-laravel-extension包,我们确保所有Laravel功能在FeatureContext立即可用。 现在,到达主要的$app对象就像app()一样简单,获得配置变量仅需config("somevar") 。 这些绑定都是自动可用的 ,随时可以使用。

使用PHPUnit的断言 (Using PHPUnit’s Assertions)

Behat doesn’t have assertions per-se. As such, you may want to use PHPUnit’s. Seeing as PHPUnit comes bundled with new Laravel apps, it’s already available, and all one needs to do to access the assertions is import the class in the FeatureContext class, like so:

Behat本身没有断言。 因此,您可能要使用PHPUnit。 看到PHPUnit与新的Laravel应用捆绑在一起,它已经可用,并且访问断言所需要做的所有事情就是将类导入FeatureContext类,如下所示:

use PHPUnit_Framework_Assert as PHPUnit;

You will then have access to assertions, like so:

然后,您将可以访问断言,如下所示:

Autocomplete dialog for assertions in imported PHPUnit class

See a full list of available assertions here.

在此处查看可用断言的完整列表。

规格书 (PhpSpec)

PhpSpec is more and more a common replacement for PHPUnit in people’s arsenals. Laravel does come with PHPUnit, but that doesn’t mean there’s no room for replacing or supplementing it with PhpSpec.

在人们的军械库中,PhpSpec越来越多地替代PHPUnit。 Laravel确实带有PHPUnit,但这并不意味着没有空间用PhpSpec替换或补充它。

The most noticeable difference between PhpSpec and PHPUnit is the syntax – PhpSpec is much more readable and human friendly, thus fitting in nicely with the whole concept of BDD and Behat. The tests don’t have to begin with the word test and the methods are all phrased as sentences, as actions we intend to do or properties we want objects to have. Even the docs say so:

PhpSpec和PHPUnit之间最明显的区别是语法– PhpSpec更具可读性和人性化,因此非常适合BDD和Behat的整个概念。 测试不必以单词test开头,方法都可以用句子表达,这是我们要执行的动作或我们想要对象具有的属性。 甚至文档都这么说:

There is no real difference between SpecBDD and TDD. The value of using an xSpec tool instead of a regular xUnit tool for TDD is the language.

SpecBDD和TDD之间没有真正的区别。 对于TDD,使用xSpec工具而不是常规xUnit工具的价值在于该语言。

In addition, PhpSpec helps with scaffolding of tests and classes, and with mocking. We’ll see how in another, more in-depth tutorial, but for now let’s install and set it up, then go through some basics.

此外,PhpSpec有助于测试和类的搭建以及模拟。 我们将在另一个更深入的教程中看到如何做,但是现在让我们安装和设置它,然后进行一些基础学习。

Let’s install PhpSpec:

让我们安装PhpSpec:

composer require phpspec/phpspec --dev

Again, we can add the installed executable to our path, so it’s runnable without the vendor/bin prefix. Either execute the command below to do so (modify the paths to match yours), or just add the whole vendor/bin folder to your path – which ever way you prefer.

同样,我们可以将已安装的可执行文件添加到我们的路径中,因此无需vendor/bin前缀即可运行。 为此,您可以执行以下命令(修改路径以匹配您的路径),也可以将整个vendor/bin文件夹添加到您的路径中(无论您喜欢哪种方式)。

sudo ln -s /home/vagrant/Code/bdd-setup/vendor/bin/phpspec /usr/local/bin/phpspec

PhpSpec is more or less ready to roll out of the box, we just need one more minor edit. In phpspec.yml in the root of our project folder, under all the lines in there, we add:

PhpSpec或多或少已准备就绪,可以立即使用,我们只需要再进行一些较小的编辑即可。 在项目文件夹根目录下的phpspec.yml中,在其中的所有行下添加:

spec_path: tests

This tells PhpSpec where to put our spec files. Feel free to change this as you wish.

这告诉PhpSpec将我们的规格文件放在哪里。 随意更改此设置。

写作规范 (Writing Specs)

Specs are classes containing tests, much like test classes in PHPUnit. To create a new spec for a class, we use the desc command (for describe). Let’s imagine we’re making a calculator class we intend to build into Laravel as a service. In version 1, a calculator should at the very least be able to sum two numbers. Let’s build this version 1.

规范是包含测试的类,非常类似于PHPUnit中的测试类。 为了为一个类创建一个新的规范,我们使用desc命令(用于describe)。 假设我们正在制作一个打算作为服务构建到Laravel中的计算器类。 在版本1中,计算器至少应能够对两个数字求和。 让我们构建这个版本1。

phpspec desc bddsetup\\Calculator

Note that bddsetup is this tutorial’s demo namespace, and you should change it to yours if you picked a different one.

请注意, bddsetup是本教程的演示名称空间,如果选择了其他名称空间,则应将其更改为您的名称空间。

This has created a specification file in tests/spec/CalculatorSpec.php, containing:

这已在tests/spec/CalculatorSpec.php创建了一个规范文件,其中包含:

<?php

namespace spec\bddsetup;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class CalculatorSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType('bddsetup\Calculator');
    }
}

Note: The $this keyword refers to the instance of the class being tested (Calculator), and not the test class itself!

注意$this关键字引用正在测试的类的实例( Calculator ),而不是测试类本身!

If we run phpspec now, it will ask us for permission to create the missing Calculator class for us. Let’s allow it.

如果我们现在运行phpspec ,它将要求我们为我们创建缺少的Calculator类的权限。 让我们允许它。

bddsetup/Calculator                                                             
  10  - it is initializable
      class bddsetup\Calculator does not exist.

                                      100%                                       1
1 specs
1 example (1 broken)
105ms

                                                                                
  Do you want me to create `bddsetup\Calculator` for you?                       
                                                                         [Y/n] 
Y
Class bddsetup\Calculator created in /home/vagrant/Code/bdd-setup/app/Calculator.php.

                                      100%                                       1
1 specs
1 example (1 passed)
135ms

This automatically passes the test because the it_is_initializable test succeeds – the class exists now, after all.

这自动通过了测试,因为it_is_initializable测试成功了-毕竟该类已经存在。

Let’s use Behat and PhpSpec in tandem to create a sum method, now.

现在,让我们串联使用Behat和PhpSpec创建一个sum方法。

二重唱 (The Duet)

In true BDD fashion, we envision a feature first, write it out, and then test for its existence. Let’s create a new feature file at features/calc.feature:

以真正的BDD方式,我们首先设想一个功能,将其写出,然后测试其存在。 让我们在features/calc.feature创建一个新的功能文件:

Feature:
  In order to make sure the calculator works
  As a developer
  I need to get the correct output from its functions

  Scenario: Summing
    Given the method "sum" receives the numbers 4 and 7
    Then the calculated value should be 11

The two definitions in the Summing scenario do not exist. We need to add them into the FeatureContext so that Behat can understand them. An easy way to generate empty snippets for us to fill out is by using the --append-snippets command.

“汇总”业务情景中的两个定义不存在。 我们需要将它们添加到FeatureContext以便Behat可以理解它们。 生成空代码段供我们填写的一种简单方法是使用--append-snippets命令。

behat --append-snippets

The FeatureContext class should now have two additional methods:

现在, FeatureContext类应该具有两个附加方法:

/**
     * @Given the method :arg1 receives the numbers :arg2 and :arg3
     */
    public function theMethodReceivesTheNumbersAnd($arg1, $arg2, $arg3)
    {
        throw new PendingException();
    }

    /**
     * @Then the calculated value should be :arg1
     */
    public function theCalculatedValueShouldBe($arg1)
    {
        throw new PendingException();
    }

Behat automatically extracted the arguments it recognized. This means the methods (and by extension the definitions) are flexible – we can alter the parameters as we see fit. Let’s fill those stubs out now.

Behat会自动提取它识别出的参数。 这意味着方法(以及扩展的定义)是灵活的–我们可以根据需要更改参数。 让我们现在填写这些存根。

/**
     * @Given the method :arg1 receives the numbers :arg2 and :arg3
     */
    public function theMethodReceivesTheNumbersAnd($arg1, $arg2, $arg3)
    {
        $this->calculator = new Calculator();
        $this->calculator->$arg1($arg2, $arg3);
    }

    /**
     * @Then the calculated value should be :arg1
     */
    public function theCalculatedValueShouldBe($arg1)
    {
        PHPUnit::assertEquals($arg1, $this->calculator->result());
    }

You can see here we’re using the PHPUnit assertions from before, despite having both PhpSpec and Behat at our disposal.

您可以在这里看到,尽管有PhpSpec和Behat都可以使用,但我们以前使用的是PHPUnit断言。

If we run Behat now, we should get:

如果我们现在运行Behat,我们应该得到:

[Symfony\Component\Debug\Exception\FatalErrorException]  
  Call to undefined method bddsetup\Calculator::sum()

That’s normal. After all, we didn’t implement it. Let’s have PhpSpec help us out with that. Add a new method into the CalculatorSpec:

那很正常 毕竟,我们没有实现它。 让我们让PhpSpec帮助我们解决这个问题。 在CalculatorSpec添加一个新方法:

function it_should_sum()
    {
        $this->sum(4, 7);
        $this->result()->shouldBe(11);
    }

When we run it, PhpSpec will ask for permission to stub out the sum and result methods:

当我们运行它时,PhpSpec将请求许可以存根sumresult方法:

> phpspec run

bddsetup/Calculator                                                               
  15  - it should sum
      method bddsetup\Calculator::sum not found.

                  50%                                     50%                    2
1 specs
2 examples (1 passed, 1 broken)
153ms

                                                                                
  Do you want me to create `bddsetup\Calculator::sum()` for you?                
                                                                         [Y/n] 
Y
  Method bddsetup\Calculator::sum() has been created.
  
bddsetup/Calculator                                                               
  15  - it should sum
      method bddsetup\Calculator::result not found.

                  50%                                     50%                    2
1 specs
2 examples (1 passed, 1 broken)
136ms

                                                                                
  Do you want me to create `bddsetup\Calculator::result()` for you?             
                                                                         [Y/n] 
Y
  Method bddsetup\Calculator::result() has been created.
  
bddsetup/Calculator                                                               
  15  - it should sum
      expected [integer:11], but got null.

                  50%                                     50%                    2
1 specs
2 examples (1 passed, 1 failed)
144ms

At the same time, the run fails because the methods don’t do what they’re expected to do. This is perfectly fine. Let’s edit the Calculator class and implement them completely now.

同时,运行失败,因为这些方法没有执行预期的操作。 很好 让我们编辑Calculator类并立即完全实现它们。

<?php

namespace bddsetup;

class Calculator
{
    protected $result = 0;

    public function sum($argument1, $argument2)
    {
        $this->result = (int)$argument1 + (int)$argument2;
    }

    public function result()
    {
        return $this->result;
    }
}

If we now run Behat with behat and PhpSpec with phpspec run, we should get all green results – all tests should pass.

如果现在使用phpspec run behat并使用phpspec run ,我们应该获得所有绿色结果–所有测试都应该通过。

It is now much easier to imagine extending the class quickly and effectively:

现在可以想象快速而有效地扩展课程变得容易得多:

  • omitting the second argument could add the one that was passed in to the result from a previous operation

    省略第二个参数可以将传入的参数添加到上一个操作的结果中
  • the sum method could return the Calculator instance to enable chaining, playing nicely with the point above

    sum方法可以返回Calculator实例以启用链接,与上面的点配合很好
  • etc…

    等等…

结论 (Conclusion)

With powerful BDD tools such as Behat and PhpSpec in place, writing out stories and testing your classes for future upgrades becomes a breeze rather than a tedious night of writing mocks.

使用功能强大的BDD工具(例如Behat和PhpSpec),编写故事并测试类以进行将来的升级变得轻而易举,而不是编写模拟游戏的繁琐之夜。

This tutorial showed you how to get started with BDD tools in a fresh Laravel application. What was shown in this post is just enough to whet your appetite. Future posts will go into more detail and some use-case specific implementations.

本教程向您展示了如何在新的Laravel应用程序中使用BDD工具。 这篇文章中显示的内容足以激发您的胃口。 以后的文章将更详细地介绍一些特定于用例的实现。

翻译自: https://www.sitepoint.com/bdd-in-laravel-getting-started-with-behat-and-phpspec/

敏捷管理中的bdd

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值