前兩週課程回顧
詳細的課程心得可以參考
上週提到了如何透過 Selenium 與 Unit Test 來進行前後搭配完整測試,透過 Unit Test 來進行最小單元測試,確保「程式照我們預期進行」,透過 E2E Testing 「確保程式可以照使用者期望執行」,透過兩者同時對 Legacy Code 進行完整測試,還有什麼比這個更能放心的去 Refactor Legacy Code 呢?
本週課程重點
本週課程將學習透過 BDD 來描述使用者需求,透過人的話語來描述測試案例,讓 PO 與 RD 間的 GAP 縮小,那什麼是透過人的話與來描述測試案例呢?
Feature: PotterShoppingCart
In order to 提供最便宜的價格給來買書的爸爸媽媽
As a 佛心的出版社老闆
I want to 設計一個哈利波特的購物車
Scenario: 第一集買了一本,其他都沒買,價格應為100*1=100元
Given 第一集買了 1 本
And 第二集買了 0 本
And 第三集買了 0 本
And 第四集買了 0 本
And 第五集買了 0 本
When 結帳
Then 價格應為 100 元
看完是否相當的直覺呢,我相信不是工程師也可以相當容易理解情境所要描述的測試案例。透過 BDD 除了可以提昇與 PO 溝通的效率,也可以自動產生 Document 與 Report 相當的方便。
如何透過 PHP 進行 BDD 測試
以下簡單的介紹如何將 feature 轉為可以測試的 PHP 程式碼, PHP 有兩套可以實踐 BDD:
以下會使用 Behat 進行範例解釋。
首先直接在 Scenario 那邊直接使用 [alt + Enter]
並且點選 Create all steps definition
建立 Feature Context ,如下圖。
預設會將 Feature Context 建置在 features/bootstrap
裡面,即會發現他已經針對每個動作建立了相對應的 method ,如下。
<?php
use Behat\Behat\Context\Context;
class FeatureContext implements Context
{
/**
* @Given /^第一集買了 (\d+) 本$/
*/
public function 第一集買了本($arg1)
{
throw new \Behat\Behat\Tester\Exception\PendingException();
}
/**
* @Given /^第二集買了 (\d+) 本$/
*/
public function 第二集買了本($arg1)
{
throw new \Behat\Behat\Tester\Exception\PendingException();
}
/**
* @Given /^第三集買了 (\d+) 本$/
*/
public function 第三集買了本($arg1)
{
throw new \Behat\Behat\Tester\Exception\PendingException();
}
/**
* @Given /^第四集買了 (\d+) 本$/
*/
public function 第四集買了本($arg1)
{
throw new \Behat\Behat\Tester\Exception\PendingException();
}
/**
* @Given /^第五集買了 (\d+) 本$/
*/
public function 第五集買了本($arg1)
{
throw new \Behat\Behat\Tester\Exception\PendingException();
}
/**
* @When /^結帳$/
*/
public function 結帳()
{
throw new \Behat\Behat\Tester\Exception\PendingException();
}
/**
* @Then /^價格應為 (\d+) 元$/
*/
public function 價格應為元($arg1)
{
throw new \Behat\Behat\Tester\Exception\PendingException();
}
}
接下來只需要將相對應 method 的行為修改一下,即可進行 BDD Testing 囉。
<?php
use App\PotterShoppingCart;
use Behat\Behat\Context\Context;
use PHPUnit_Framework_Assert as PHPUnit;
class FeatureContext implements Context
{
private $potterShoppingCart;
private $total_price;
public function __construct()
{
$this->potterShoppingCart = new PotterShoppingCart();
}
/**
* @Given /^第一集買了 (\d+) 本$/
* @param int $number
*/
public function 第一集買了本(int $number)
{
$this->potterShoppingCart->addBook(1, $number);
}
/**
* @Given /^第二集買了 (\d+) 本$/
* @param int $number
*/
public function 第二集買了本(int $number)
{
$this->potterShoppingCart->addBook(2, $number);
}
/**
* @Given /^第三集買了 (\d+) 本$/
* @param int $number
*/
public function 第三集買了本(int $number)
{
$this->potterShoppingCart->addBook(3, $number);
}
/**
* @Given /^第四集買了 (\d+) 本$/
* @param int $number
*/
public function 第四集買了本(int $number)
{
$this->potterShoppingCart->addBook(4, $number);
}
/**
* @Given /^第五集買了 (\d+) 本$/
* @param int $number
*/
public function 第五集買了本(int $number)
{
$this->potterShoppingCart->addBook(5, $number);
}
/**
* @When /^結帳$/
*/
public function 結帳()
{
$this->total_price = $this->potterShoppingCart->getPrice();
}
/**
* @Then /^價格應為 (\d+) 元$/
* @param $expected
*/
public function 價格應為元($expected)
{
PHPUnit::assertEquals($expected, $this->total_price);
}
}
修改完成後執行測試。
一樣會有紅綠燈可以查看,但還沒結束唷,還可以透過 BDD 來產生文件,在這邊我使用了 「BehatHtmlFormatterPlugin」這個套件來產生漂亮的 HTML 文件。
安裝完成之後先於根目錄中增加 behat.yml
,範例如下:
# behat.yml
default:
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
即可於 build/html/behat/
資料夾中看到已經 build 好的 html 網頁。
看到這邊,是不是覺得既簡單又好使用呢?
總結
上完課程後對於 TDD 與 BDD 不管是觀念還是實戰演練都有了更深刻的體會,老師上課透過完整的 Lab 題目讓大家有非常非常多的情境可以進行練習,就連我這個沒有在寫 C# 且沒有用過 Visual Studio 的工程師來說,要跟上老師的課程也是蠻 OK 的,由於老師上課分享的內容實在太豐富了,於本 Blog 中僅能擷取一些重點進行分享,扎實的課程與實戰體驗就是在課程上課已學到的,如果大家尚未使用 TDD 進行開發或是已經有在使用但不是很瞭解或不知該如何導入專案中,相當推薦大家可以來上「自動測試與 TDD 實務開發」Joey ( 91 哥 ) 的課程,詳細請 Follow SkillTree,也感謝 91 哥這三天來的指導與分享,真的獲益良多。