自動測試與 TDD 實務開發(使用 C#) by 91 哥 Day3

前兩週課程回顧

詳細的課程心得可以參考

上週提到了如何透過 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 哥這三天來的指導與分享,真的獲益良多。

自動測試與 TDD 實務開發(使用 C#) by 91 哥 Day2

上週課程回顧

詳細的上週課程心得可以參考「自動測試與 TDD 實務開發(使用 C#) by 91 哥 Day1」。

上週課程提到,什麼是 Unit Test ,好的單元測試需要吻合 F.I.R.S.T 原則,如何針對單一功能撰寫單元測試,了解開發流程與方式,了解業務單位需求撰寫需求文件與單元測試,針對單元程式建構出來的程式架構完成 Production Code 相關邏輯。

有了單元測試,就算需求來了想要調整 Production Code 都不用怕,因為有了單元測試,單元測試會告訴開發者,程式是否還是可以正常的運作(綠燈),如果為紅燈就知道有功能被改壞了,可以「即時」發現問題並且進行修正。

透過 Code Coverage 可以了解我們的單元測試的完整度,是否有遺漏的測試案例。

本週課前提要

上週學習了 Unit Test 但可能還是無法直接套用到正在進行中的專案,因為專案上可能都是滿滿的 Legacy Code ,無法針對 Legacy Code 進行測試,需求不斷的來招架不住該怎麼辦?我想今天的課程就是重點了,透過 E2E Testing 、 Unit Test 與 Refactor 來教你,「如何安安心心 Refactor Legacy Code 還可以完成業務單位新需求」。

該還的技術債還是要還,如果只是在 Legacy Code 上疊床架屋, Code 只會越來越亂, Bug 只會越來越多,只會讓後面維護的工程師越來越不懂這個系統的原貌,趁早治療才是明智之舉,千萬不要放棄治療。

本週課程前的小故事

老闆:「Hash,今天有個客戶跟我說他想要在我們的購物車上面加上可以自動進行折扣的功能,明天可以完成嗎?」

Hash:「痾…. (Hash的內心話:雖說只是加幾行 Code 就可以搞定,但… 會不會改壞其他東西阿)」

老闆:「怎樣?」

Hash(心虛的說):「好…」

本週課程重點

  • TDD
  • End-To-End Testing
  • Refactor

End-To-End Testing

課程中透過 Selenium 來指導學員如何透過他來對網頁進行 E2E Testing , Selenum 除了可以直接針對網站進行測試外還可以匯出 C# 測試原始碼直接無痛整合進行 Visual Studio ,讓 E2E Testing 與 UnitTest 一起來,進而達到完整測試。

上課採雷心得分享: Firefox 的 Selenium 再 Firefox v46.1 之後的版本有 Bug 。

Refactor

終於來到本週最為精采的「如何針對 Legacy Code 進行 Refactor」,結合網頁測試與單元測試來達成我們的目的 Refactor 。

當我們有了 E2E Testing 可以針對特定功能撰寫網頁測試案例,以使用者為出發點操作系統功能,記錄使用者的行為與預期結果。

當我們有了單元測試,針對最小的功能進行測試,預期這些功能方法的行為與其回傳值。

有了他們,修改程式後馬上進行完整 Unit Test 修改程式碼的方法後是否還吻合我們的測試案例預期行為,若為綠燈,則接著進行 E2E Testing 測試是否使用者還可以如預期的操作系統功能,修改程式碼超安心,一點都不擔心會不會改壞程式碼,還有什麼比這個還要更棒的呢?

91 哥補充:重構過程中千萬不可增加新功能,一次只做一件事情,重構完成 -> 綠燈 -> 增加新功能

本週課程後的小故事

同上一樣的故事內容

老闆:「Hash,今天有個客戶跟我說他想要在我們的購物車上面加上可以自動進行折扣的功能,明天可以完成嗎?」

Hash(心理想著):「有了 91 哥教我們的測試方法,我想花個一天把相關 Legacy Code Refactor 一下,半天完成老闆功能,沒問題!!」

Hash(有自信的說):「好老闆,明天下午給你!」

老闆:「太好了,謝謝你的幫忙」

其實一樣的故事內容課前與課後Hash的回答的方式是完完全全不一樣的,就算今天要花時間整理 Legacy Code 而無法如期交出功能,也可以跟老闆提出需要進行程式碼重構進而提升系統穩定度,我想老闆的接受度也是高些的。

本週課程心得

就如同小故事告訴我們的,透過完整的測試流程與方法,可以讓工程師們在修改程式上更為有自信且更為安心,其實只要隨手 Refactor 一隻 Method 與補上幾隻測試程式,就可以達到讓整個專案穩定度提升。

《Clean Code》 文中提到,童子軍原則:「離開營地前,讓營地使用前更加乾淨」,同樣的原則也可以透用在軟體開發上,程式碼在 Commit 前只要可以讓 Code Coverage 提昇些,讓程式碼看起來更整齊乾淨就是進步。

課程中相關學習資源

Hexo 採雷大小事

前言

在使用 Hexo 的過程中,總是會遇到官方文件沒有寫的很詳細、不知道該如何使用或是有問題的功能,這邊把我有遇到的採雷狀況記錄在這邊。

GitHub page custom domain 會跑掉

每當我重新 deploy 上去 github 後,custom domain 就會被清空,後來發現只要在 source 目錄下新增 CNAME這個檔案,內容是您所希望的 URL 如 hashman.tw or www.hashman.tw ,神奇的事情發生了,真的就不會被清空了。

依照 hexo next 上面的文件去進行 config 的設定,卻不 work

一開始直接都是更新根目錄上的 _config.yml 按著官方文件上面來操作,但 Blog 就是不會變更,最後發現原來是要修改 theme/next 中的 _config.yml 才是,修改後一切正常。

參考資料

使用 Hexo + GitHub

什麼是 Hexo

Hexo 是一套用 NodeJS 寫的 blog framework ,可以透過 hexo 的指令非常快速的建立文章,以下會有簡易指令教學

另外這有記錄我在使用 Hexo 時的採雷狀況與解決方法 – Hexo 採雷大小事

什麼是 GitHub Page

GitHub Page 提供所有 GitHub 的用戶可以直接將靜態網頁直接放上 GitHub 中,即可直接於 https://[account].github.io 查看網頁內容的服務。

如何使用 GitHub Page

直接於自己的 GitHub 帳號直接建立一個 [account].github.io 的 Public Repo ,於 Repo 中上傳靜態網頁即可在 https://[account].github.io 上查看網頁。

為什麼想要使用 Hexo + GitHub

  1. Hexo 為 Open Source 軟體可以非常自由的依照自己的需求去調整 Source Code 。
  2. 客製化的網址需求,雖說 LogDown 也可以,但… 需要付費才可以使用,其實還蠻貴的。
  3. GitHub Public Repository 無空間上限(嚴格來說不是無上限,請參考: GitHub – What is my disk quota?)。
  4. 主機交給 GitHub 管,只需要單純的上 Code 就好不需要去架設與管理主機,相對穩定。
  5. 多了個機會可以了解 GitHub Page 這個服務,詳細參考這裡
  6. 幫自己的 GitHub Contributions 多一點色彩,每更新一次 Blog 都會 deploy 到 GitHub 上,也就會增加自己的 Contributions 。

Hexo Command

// 第一次使用需要先 init hexo folder 與安裝套件
$ hexo init <folder> // init hexo folder
$ cd <folder>
$ npm install
// 新增文章
$ hexo new [layout] <title> // or hexo n [layout] <title>
// layout 可以不要填寫
// title 建議使用雙引號刮起來
// 產生靜態檔
$ hexo generate // or hexo g
// 發表草稿,這個功能我蠻少用的
$ hexo publish [layout] <filename>
// 啟用 hexo 伺服器
$ hexo server // or hexo s
// Deploy 至 remote
$ hexo deploy // or hexo d
// 清除快取檔案
$ hexo clean

Hash 使用 Blog 的歷史

參考資料

自動測試與 TDD 實務開發(使用 C#) by 91 哥 Day1

今天第一天上 91 哥的課,也是第一次,看到整間教室坐滿就知道 91 哥的名氣跟課程內容的豐富度一定很高,課程中的學員也有蠻多都是已經上過 91 哥課程的老學員又回來在聽的,隨著時間的增長,課程中的實務案例與經驗分享也更為豐富。

前景提要

  • 在 KKBOX 其實已經有一定的 Unit Test 與 CI 自動化的導入機制,所以其實在上這門課以前,我就已經有不少的測試開發經驗,雖說大部分都還是撰寫 Unit Test ,但對於 TDD 也是有一定的基礎在。
  • 91 哥上課使採用 C# 上課,上課前 91 哥都會提供相當仔細的課程環境建設,如果你本身有在寫 C# 那恭喜你在這邊應該是沒有任何問題的,但如果你是 Mac or 沒有寫過 C# 的開發者那 … 你應該在於建置環境上面需要多花點心力處理,以下提供幾個朋友實際安裝環境所會費的時間參考。
    • Mac + Virtual Box + Visual Studio(Full install) 在 SSD 上面跑約需要安裝 1.5 hr 。
    • Mac + Virtual Box + Visual Studio(上課所需的內容) 在 SD 上面跑約需要安裝 2.5 hr 。
  • 如果是用 C# 上課對於一個非 C# 的開發者會不會有困難呢?
    • 以我的狀況,我是 PHP 的開發者,平常是使用 PHPStorm 進行開發,對於 IDE 並不陌生,但是面對號稱地表最強的 IDE Visual Studio 說真的還是需要花點時間去習慣。
    • 至於 Code 會不會看不懂?我個人是覺得不會,只是 C# 有些語法上面是需要去習慣一下的。
  • 如果不是開發者像是 PM or Manager 去上這門課適合嗎?
    • 因為 91 哥上課會有大量的 workshop 需要實際去寫 code 進行相關測試,設計觀念與寫 Code 的比例大約是 6:4 吧,我是覺得還是會有收穫的,但可能在於寫 Code 進行測試上面可能稍有些辛苦。

課程重點

  1. 不是教你寫測試程式,而是教你開發跟設計
  2. 如何透過測試同時達到測試方法與測試文件
  3. 如何開發出更為穩定更為安心可以早早下班洗洗睡的 Code

有趣的上課方式

  1. 生動活潑的上課方式
    • 上課會採用分組活動來進行討論與分享。
    • 記點獎勵來激勵大家多多回答與發問。
    • 有問必達,可以於上課中隨時提出問題 91 哥都會耐心的解答,或是也可以利用備忘錄或是下課詢問的方式也可以獲得你要的答案 。
  2. 學習地圖與強調學習重點
    • 上課一開始就將 TDD Skill Tree 整個展開給學員們看,非常明瞭三天的學習方向、學習步驟與關連。
    • 本次開始提供課前講義(填空版與答案版),填空版主要為複習使用,可以知道空格的地方都是上課的重點,答案版可以隨時尋找答案。
  3. 實戰演練
    • 透過 Lab 的題目(原始題目、答案與步驟)進行操作,更可以針對不同的情境去撰寫自己的測試方法。
    • 透過老師提供的步驟說明,在自己回家後如果對於 Lab 有不清楚的時候可以翻開步驟一步一步的跟著進行操練。

了解實務上所遇到的問題與其解法

雖說 KKBOX 已經是一個已經有導入自動化測試開發的公司,但還是有需多的專案都是 0 測試的,如果將這些 0 測試的專案開始導入測試也是一個 Big Step 。

在實務上面常常會遇到測試對象與外部服務相依的狀況,應該要將這些外部服務切斷進而達到測試的獨立性。

常常也會遇到測試覺得複雜且不容易,也有可能說明了測試的對象並非為最小的測試單元或真的這個 Method 的業務邏輯相當複雜,進而可能需要進行 Refactor 或是確認需求等。

心得

在上課前總是會覺得,測試真的很麻煩,寫測試會增加我們的開發時間,但上完第一週的課程後,我了解了掌握開發的技巧、方法與順序,了解開發的重點,其實會發現測試一點都不花時間,掌握問題並且解決問題,開發出更有信心且穩定的程式碼是我們該邁向的目標。

期待未來兩週的課程。