Laravel BDD(行為驅動開發)

Gary Ng
11 min readAug 31, 2022

--

什麼是 BDD, BDD 是使用自然語言去撰寫的一份可以執行的規格書, 且該規格書因為是使用自然語言,因此在沒有程式背景的人也能理解該功能在做什麼事情以及會有什麼結果。

而 BDD 主要使用的工具為 cucumber, 而 cucumber 又有一些基本觀念要理解, Specification, Step, Scenario

  1. 每一個 Scenario 以面會有多個 Step
  2. 多個相似的 Scenario 可以寫在同一份 Specification 裡
  3. 在 cucumber 裡一個 specification 也被稱為 feature

我們先來看一下基本的 BDD 基本結構

Feature: 簡單說明規格書功能


Scenario: 測試的案例
Given API 網址為 "/v2/auth/register"
When以 "POST" 方法要求 API
Then 回傳狀態應為 400
And 回傳的錯誤訊息為 "發生錯誤"

看完了基本結構後我們發現此結構有 Feature, Scenario, Given, When, Then, And 等單字, 我們接下來對各單子進行解說

  1. Feature: (功能)
主要用於說明該規格書所涵蓋的功能為何

2. Scenario(場景)

要測試的案例敘述

3. Given(假定)

給予什麼條件或者資料

4. When (當)

何時發生或者怎麼發生

5. Then (那麼)

發生某些條件之後結果為何

6. And (而且)

"而且"。 當有多個 Given 時可以使用, 使其更符合語意

7. But (但是)

"但是"。當有多個 Then 時可以使用,使其更符合語意

了解基本的 BDD 基本結構後我們直接來進行實作,以下為使用 Laravel 進行 BDD。

首先我們先用 composer 安裝 BDD 的套件

composer require behat/behat --dev

接著進行初始化

./vendor/bin/behat --init

初始化完後會產生一個 features 的資料夾

Ps: 裡面只會有 features/bootstrap/FeatureContext.php, 其餘的檔案是我自己額外新增的!!!!

而我們打開 FeatureContext.php 會看到如下內容, 會發先剛檔案只 implement Context

<?php

use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;

/**
* Defines application features from the specific context.
*/
class FeatureContext implements Context
{
/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/
public function __construct()
{
}
}

但是在這邊我們要自己設定測試的 setup 以及 teardown method 以及自定義 env file

<?php

use
Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Illuminate\Contracts\Console\Kernel;

/**
* Defines application features from the specific context.
*/
class
FeatureContext extends \Illuminate\Foundation\Testing\TestCase implements Context
{

protected const
ENV_FILE = '.env.behat';

/**
*
@var \Illuminate\Foundation\Application
*/
protected static $contextSharedApp
;

/**
*
@return \Illuminate\Foundation\Application
*/
public function
createApplication()
{
$app = require __DIR__ . '/../../bootstrap/app.php';
// 自訂義要載入的 env file
$app->loadEnvironmentFrom(self::ENV_FILE);

$app->make(Kernel::class)->bootstrap();

return $app;
}

/**
*
@BeforeScenario
*/
public function
before(): void
{
if (!static::$contextSharedApp) {
parent::setUp();
static::$contextSharedApp = $this->app;
} else {
$this->app = static::$contextSharedApp;
}

}

/**
*
@AfterScenario
*/
public function
after(): void
{
if (static::$contextSharedApp) {
parent::tearDown();
static::$contextSharedApp = null;
}
}
}

定義完之後我們可以在 features 的資料夾新增一個 register.feature 檔案, 用以描述註冊會員的時候會有哪些操作以及描述規格書, 而 feature 的檔案我們也可以設定他要使用的語系, 我們這邊設定成繁體中文, 假如設定成繁體中文的話, Feature (功能), Given(假定), When(當), Then(那麼), And(而且)

#language: zh-TW

@auth @authRegister
功能: 使用者註冊會員

@authRegisterNoData
場景: 前端沒有傳入任何參數
假定 API 網址為 "/v2/auth/register"
以 "POST" 方法要求 API
那麼 回傳狀態應為 400
而且 回傳的錯誤訊息為 "請輸入帳號或者密碼"

ps: 這邊看到的 @auth, @authRegister 為 tag, 主要用針對 Feature 或者 Scenario 下標籤, 這樣到時可以只執行特定標籤等

這邊定義完之後我們接著在根目錄下新增 behat.yml 檔案,用以設定 behat 的一些基本設定

default:
suites:
auth_features:
paths:
- "%paths.base%/features/tests/auth"
contexts:
- ApiFeatureContext # API 相關資訊
- DatabaseAssertionContext # Database 相關

設定完之後再執行一次

vendor/bin/behat --init 
此指令會幫我們產生 ApiFeatureContext 以及 DatabaseFeatureContext

ApiFeatureContext 如下

/**
* Defines application features from the specific context.
*/
class
ApiFeatureContext extends FeatureContext
{
protected $apiUrl = '';

protected $apiBody = [];

protected $response;

/**
*
@Given API 網址為 :apiUrl
*
*
@param string $apiUrl
*/
public function
apiUrl(string $apiUrl)
{
$this->apiUrl = $apiUrl;
}

/**
*
@Given API 附帶資料為
*
*
@param TableNode $tableNode
*/
public function
apiBody(TableNode $tableNode)
{
$this->apiBody = $tableNode->getHash()[0];
}

/**
*
@When 以 :method 方法要求 API
*
*
@param string $method
*/
public function
request(string $method)
{
$this->response = $this->json($method, $this->apiUrl, $this->apiBody);
}

/**
*
@Then 回傳狀態應為 :statusCode
*
*
@param int $statusCode
*/
public function
assertStatus(int $statusCode)
{
$this->response->assertStatus($statusCode);
}
}

DatabaseFeatureContext 如下

/**
* Defines application features from the specific context.
*/
class
DatabaseAssertionContext extends FeatureContext
{
/**
*
@Then 資料表 :tableName 應有資料
*
*
@param string $tableName - 資料表名稱
*
@param TableNode $tableNode
*/
public function
assertTableRecordExisted(string $tableName, TableNode $tableNode)
{
$this->assertDatabaseHas($tableName, $tableNode->getHash()[0]);
}
}

其實到這邊我們可以發現 Context 主要就是定義會出現在 Feature 上面的文案

接著可以透過以下指令進行跑 BDD 測試

./vendor/bin/behat --format pretty --verbose

然後假如要生成 bdd 報表的話需要安裝

composer require emuse/behat-html-formatter --dev

安裝完成後再 behat.yml 設定如下內容

formatters:
html:
output_path: "%paths.base%/build/html/behat"

extensions:
emuse\BehatHTMLFormatter\BehatHTMLFormatterExtension:
name: html
renderer: Twig,Behat2
file_name: index
print_args: true
print_outp: true
loop_break: true

再透過以下指令產生檔案

./vendor/bin/behat --format html --verbose

參考資料:
https://docs.behat.org/en/v2.5/guides/1.gherkin.html#tags

https://docs.behat.org/en/latest/user_guide/context/hooks.html

--

--

Gary Ng
Gary Ng

Written by Gary Ng

軟體工程師、後端工程師

No responses yet