歡迎來到有關使用行為驅動開發 (BDD) 方法開發 Laravel 應用程式的系列。全端 BDD 看起來很複雜且令人生畏。有多少開發人員就有多少種實現此目的的方法。
在本系列中,我將引導您了解使用 Behat 和 PhpSpec 從頭開始設計 Laravel 應用程式的方法。
#一般來說,關於 BDD 的資源很多,但 Laravel 的具體資料很難找到。因此,在本系列中,我們將更專注於 Laravel 相關方面,而不是您可以在許多其他地方閱讀的一般內容。
在描述行為(也稱為編寫故事和規範)時,我們將使用由外而內的方法。這意味著每次我們建立一個新功能時,我們都會從編寫整個使用者故事開始。這通常是從客戶或利害關係人的角度來看的。
當我們這樣做時,我們期望發生什麼? 块引用>除非我們重構現有程式碼,否則除非我們遇到失敗的紅色步驟,否則我們不允許編寫任何程式碼。
有時,有必要迭代地解決我們正在處理的故事或功能(我交替使用的兩個詞)的一小部分。這通常意味著使用 PhpSpec 編寫規格。有時,在整個故事(在接受層級上)通過之前,需要在整合或單元層級上進行多次迭代。這一切聽起來很複雜,但事實並非如此。我堅信邊做邊學,所以我認為一旦我們開始寫一些實際的程式碼,一切都會變得更有意義。
我們將在四個不同的層面上寫故事和規範:
1. 驗收
大多數時候,我們的功能套件將充當我們的接受層。我們在功能套件中描述功能的方式將與我們編寫接受故事的方式(使用自動化瀏覽器框架)非常相似,因此會產生大量重複。
只要故事從客戶的角度描述了行為,它們就可以作為接受故事。我們將使用 Symfony DomCrawler 來測試應用程式的輸出。在本系列的後面,我們可能會發現我們需要透過也可以執行 JavaScript 的實際瀏覽器進行測試。透過瀏覽器進行測試增加了一些新的問題,因為我們需要確保在套件運行時加載小時測試環境。
2. 功能性
在我們的功能套件中,我們將可以存取Laravel應用程序,這非常方便。首先,它可以輕鬆區分環境。其次,不要透過瀏覽器使我們的測試套件更快。每當我們想要實作一個新功能時,我們都會使用 Behat 在我們的功能套件中編寫一個故事。
3. 集成
我們的整合套件將測試應用程式核心部分的行為,這些部分不一定需要存取 Laravel。整合套件通常是 Behat 故事和 PhpSpec 規範的混合體。
4.單位
我們的單元測試將以 PhpSpec 編寫,並將測試應用程式核心的獨立小單元。我們的實體、值物件等都會有規格。
案例
在本系列中,從下一篇文章開始,我們將建立一個追蹤時間的系統。我們將首先透過編寫 Behat 特徵來描述外在行為。我們的應用程式的內部行為將使用 PhpSpec 進行描述。
這兩個工具一起將幫助我們對正在建立的應用程式的品質感到滿意。我們將主要在三個層面上編寫功能和規格:
- 功能性
- 集成
- 單位
在我們的功能套件中,我們將以無頭模式抓取應用程式的 HTTP 回應,這意味著我們不會通過瀏覽器。這將使與 Laravel 互動變得更容易,並使我們的功能套件也充當我們的接受層。
稍後,如果我們最終擁有更複雜的 UI 並且可能還需要測試一些 JavaScript,我們可能會添加專用的驗收套件。該系列仍在進行中,因此請隨時在評論部分提出您的建議。
我們的設定
請注意,在本教程中,我假設您全新安裝並運行了 Laravel (4.2)。最好您也使用 Laravel Homestead,這就是我在編寫此程式碼時使用的。
在開始任何實際工作之前,讓我們確保 Behat 和 PhpSpec 已啟動並運行。首先,每當我開始一個新的 Laravel 專案時,我喜歡做一些清理工作並刪除我不需要的東西:
git rm -r app/tests/ phpunit.xml CONTRIBUTING.md如果您刪除這些文件,請務必相應地更新您的
composer.json
文件:"autoload": { "classmap": [ "app/commands", "app/controllers", "app/models", "app/database/migrations", "app/database/seeds" ] },當然:
$ composer dump-autoload現在我們準備好引進我們需要的 BDD 工具了。只需將
require-dev
部分新增到您的composer.json
:"require": { "laravel/framework": "4.2.*" }, "require-dev": { "behat/behat": "~3.0", "phpspec/phpspec": "~2.0", "phpunit/phpunit": "~4.1" },“我们为什么要引入 PHPUnit?”你可能在想?在本系列中,我们不会编写优秀的 PHPUnit 测试用例,但断言与 Behat 一起是一个方便的工具。我们将在本文后面编写第一个功能时看到这一点。
请记住在修改
composer.json
后更新依赖项:$ composer update --dev我们即将完成安装和设置。 PhpSpec 开箱即用:
$ vendor/bin/phpspec run 0 specs 0 examples 0ms但是 Behat 需要使用
--init
选项快速运行才能设置所有内容:$ vendor/bin/behat --init +d features - place your *.feature files here +d features/bootstrap - place your context classes here +f features/bootstrap/FeatureContext.php - place your definitions, transformations and hooks here $ vendor/bin/behat No scenarios No steps 0m0.14s (12.18Mb)第一个命令创建了一个闪亮的新
FeatureContext
类,我们可以在其中编写功能所需的步骤定义:<?php use Behat\Behat\Context\SnippetAcceptingContext; use Behat\Gherkin\Node\PyStringNode; use Behat\Gherkin\Node\TableNode; /** * Behat context class. */ class FeatureContext implements SnippetAcceptingContext { /** * Initializes context. * * Every scenario gets its own context object. * You can also pass arbitrary arguments to the context constructor through behat.yml. */ public function __construct() { } }编写我们的第一个功能
我们的第一个功能非常简单:我们只需确保新的 Laravel 安装以“您已到达”来迎接我们。在主页上。我还添加了一个相当愚蠢的
Given
步骤,假设我登录了
,这仅用于显示在我们的功能中与 Laravel 交互是多么容易。从技术上讲,我将这种类型的功能归类为功能测试,因为它与框架交互,但它也可以作为验收测试,因为我们不会看到通过浏览器测试运行类似测试有任何不同的结果工具。现在我们将坚持使用我们的功能测试套件。
继续创建一个
welcome.feature
文件并将其放入features/functions
:# features/functional/welcome.feature Feature: Welcoming developer As a Laravel developer In order to proberly begin a new project I need to be greeted upon arrival Scenario: Greeting developer on homepage Given I am logged in When I visit "/" Then I should see "You have arrived."通过将功能特性放在
featured
目录中,我们以后可以更轻松地管理我们的套件。我们不希望不需要 Laravel 的集成类型功能必须等待缓慢的功能套件。我喜欢让事情保持美观和干净,所以我相信我们应该为我们的功能套件提供一个专用的功能上下文,以便我们可以访问 Laravel。您可以继续复制现有的
FeatureContext
文件并将类名称更改为LaravelFeatureContext
。为此,我们还需要一个behat.yml
配置文件。在项目的根目录中创建一个并添加以下内容:
default: suites: functional: paths: [ %paths.base%/features/functional ] contexts: [ LaravelFeatureContext ]我认为这里的 YAML 是非常不言自明的。我们的功能套件将在
functioning
目录中查找功能,并通过LaravelFeatureContext
运行它们。如果我们此时尝试运行 Behat,它会告诉我们实现必要的步骤定义。我们可以使用以下命令让 Behat 将空脚手架方法添加到
LaravelFeatureContext
中:$ vendor/bin/behat --dry-run --append-snippets $ vendor/bin/behat Feature: Welcoming developer As a Laravel developer In order to proberly begin a new project I need to be greeted upon arival Scenario: Greeting developer on homepage # features/functional/welcome.feature:6 Given I am logged in # LaravelFeatureContext::iAmLoggedIn() TODO: write pending definition When I visit "/" # LaravelFeatureContext::iVisit() Then I should see "You have arrived." # LaravelFeatureContext::iShouldSee() 1 scenario (1 pending) 3 steps (1 pending, 2 skipped) 0m0.28s (12.53Mb)现在,正如您从输出中看到的那样,我们已准备好开始实施第一个步骤:
鉴于我已登录
。Laravel 附带的 PHPUnit 测试用例允许我们执行诸如
$this->be($user)
之类的操作,它会登录给定用户。最终,我们希望能够像使用 PHPUnit 一样与 Laravel 交互,所以让我们继续编写“我们希望拥有”的步骤定义代码:/** * @Given I am logged in */ public function iAmLoggedIn() { $user = new User; $this->be($user); }这当然行不通,因为 Behat 不知道 Laravel 的具体内容,但我很快就会向您展示让 Behat 和 Laravel 很好地协同工作是多么容易。
如果您查看 Laravel 源代码并找到
Illuminate\Foundation\Testing\TestCase
类(默认测试用例扩展自该类),您会发现从 Laravel 4.2 开始,一切都已更改转移到一个特质。ApplicationTrait
现在负责启动Application
实例、设置 HTTP 客户端并为我们提供一些辅助方法,例如be()
。这非常酷,主要是因为这意味着我们可以将其拉入我们的 Behat 上下文中,几乎不需要任何设置。我们还可以访问
AssertionsTrait
,但这仍然与 PHPUnit 相关。当我们引入特征时,我们需要做两件事。我们需要一个
setUp()
方法,如Illuminate\Foundation\Testing\TestCase
类中的方法,并且需要一个createApplication()
方法,如默认 Laravel 测试用例中的方法。其实我们直接复制这两个方法就可以了。只有一件事需要注意:在 PHPUnit 中,方法
setUp()
会在每次测试之前自动调用。为了在 Behat 中实现相同的效果,我们可以使用@BeforeScenario
注释。将以下内容添加到您的
LaravelFeatureContext
:use Illuminate\Foundation\Testing\ApplicationTrait; /** * Behat context class. */ class LaravelFeatureContext implements SnippetAcceptingContext { /** * Responsible for providing a Laravel app instance. */ use ApplicationTrait; /** * Initializes context. * * Every scenario gets its own context object. * You can also pass arbitrary arguments to the context constructor through behat.yml. */ public function __construct() { } /** * @BeforeScenario */ public function setUp() { if ( ! $this->app) { $this->refreshApplication(); } } /** * Creates the application. * * @return \Symfony\Component\HttpKernel\HttpKernelInterface */ public function createApplication() { $unitTesting = true; $testEnvironment = 'testing'; return require __DIR__.'/../../bootstrap/start.php'; }非常简单,看看我们运行 Behat 时得到的结果:
$ vendor/bin/behat Feature: Welcoming developer As a Laravel developer In order to proberly begin a new project I need to be greeted upon arival Scenario: Greeting developer on homepage # features/functional/welcome.feature:6 Given I am logged in # LaravelFeatureContext::iAmLoggedIn() When I visit "/" # LaravelFeatureContext::iVisit() TODO: write pending definition Then I should see "You have arrived." # LaravelFeatureContext::iShouldSee() 1 scenario (1 pending) 3 steps (1 passed, 1 pending, 1 skipped) 0m0.73s (17.92Mb)绿色的第一步,这意味着我们的设置正在运行!
接下来,我们可以实现
当我访问
步骤。这个非常简单,我们可以简单地使用ApplicationTrait
提供的call()
方法。一行代码即可实现:/** * @When I visit :uri */ public function iVisit($uri) { $this->call('GET', $uri); }最后一步,
然后我应该看到
,需要更多一点,我们需要引入两个依赖项。我们需要 PHPUnit 来进行断言,并且需要 Symfony DomCrawler 来搜索“您已到达”。文本。我们可以这样实现:
use PHPUnit_Framework_Assert as PHPUnit; use Symfony\Component\DomCrawler\Crawler; ... /** * @Then I should see :text */ public function iShouldSee($text) { $crawler = new Crawler($this->client->getResponse()->getContent()); PHPUnit::assertCount(1, $crawler->filterXpath("//text()[. = '{$text}']")); }这与您使用 PHPUnit 时编写的代码几乎相同。
filterXpath()
部分有点令人困惑,我们现在不用担心它,因为它有点超出了本文的范围。请相信我,它有效。最后一次跑步是个好消息:
$ vendor/bin/behat Feature: Welcoming developer As a Laravel developer In order to proberly begin a new project I need to be greeted upon arival Scenario: Greeting developer on homepage # features/functional/welcome.feature:6 Given I am logged in # LaravelFeatureContext::iAmLoggedIn() When I visit "/" # LaravelFeatureContext::iVisit() Then I should see "You have arrived." # LaravelFeatureContext::iShouldSee() 1 scenario (1 passed) 3 steps (3 passed) 0m0.82s (19.46Mb)该功能正在按预期工作,并且开发人员在抵达时受到欢迎。
结论
完整的
LaravelFeatureContext
现在应该与此类似:call('GET', $uri); } /** * @Then I should see :text */ public function iShouldSee($text) { $crawler = new Crawler($this->client->getResponse()->getContent()); PHPUnit::assertCount(1, $crawler->filterXpath("//text()[. = '{$text}']")); } }随着我们继续使用 BDD 开发新的 Laravel 应用程序,我们现在已经有了一个非常好的基础。我希望我已经向您证明了让 Laravel 和 Behat 很好地协同工作是多么容易。
我们在第一篇文章中讨论了许多不同的主题。不用担心,随着本系列的继续,我们将更深入地研究一切。如果您有任何问题或建议,请留言。
以上是讓我們從 Laravel、BDD 和你開始的詳細內容。更多資訊請關注PHP中文網其他相關文章!