阅读视图

发现新文章,点击刷新页面。
🔲 ☆

練好基本功

September 19 21:00~21:35

 

今天跟ezKanban團隊mobbing,Teddy說:「我這兩天跟別人講理,你們有沒有注意到一件事情,就是平常mobbing我會一直問你們定義。我知道定義,所以我不怕跟別人講理。」

定義,很重要,也很無聊。想走捷徑的人,只看到別人光鮮亮麗的表皮,看不到別人蹲馬步時的付出。抄襲了皮,外表漂漂亮亮,骨子裏還是Teddy的指導教授常說的那句話:「只有薄薄的一層,一刮就掉」

這不是,Teddy才說一句話,一個定義,一刮就掉。

Teddy在北科上課會告訴學生:「Teddy和學生之間是平行關係,不是上下關係,Teddy只是在課堂上扮演老師的角色而已。上課Teddy會問你們很多問題,你們對我說的話如果有疑問,也可以直接說。如果我說錯了,最好可以打臉我。」

Teddy帶的學生,應該都覺得Teddy很嚴格,甚至嚴厲。Teddy常跟他們說:「實驗室很單純,外面世界很黑暗,壞人很多。你不把專業能力練好,出去很容易被壞人唬得一愣一愣的。」我好歹也是在業界工作的人,先讓你們體驗一點人性險惡,以後出校門才有抵抗力。

壞人的專業能力不會比你好,但他強在「夠壞」。這一點,我們永遠比不上,也不想比上。

***


Teddy在外面開課,都是先寫部落格文章,然後再整理成上課教材。Teddy可以說,收費課程的教材內容,絕大部分我都放在部落格上。Teddy做事不會留一手,知道什麼寫什麼,並提供參考資料。我巴不得鄉民們追朔源頭,多讀書。

沒錢上課的學員,只要肯學,把「搞笑談軟工」部落格文章讀完、讀懂,Teddy相信在軟工這一塊,絕對可以排進台灣軟體工程師的前10%。付費上課的學員,是Teddy的衣食父母,沒有他們,泰迪軟體就經營不下去。

這就是Teddy照顧小白、普通人、大神、有錢人、沒錢人的方法。

所以,Teddy不主動參加社群活動,把時間留下來專心讀書、寫作、寫程式,還有這幾個月開始新的嘗試:錄製YouTube影片。以前的敏捷高峰會和這幾年的DDD TW年會,都是主辦單位看得起Teddy,主動邀請參加。不邀請了,人家不需要了,也就不參加了。

台灣的社群活動,主辦單位很辛苦,也有很負責的講師。但是也有一群人,Teddy沒有實際測量過,但希望是一小群人,到處刷存在感、打知名度。這種人,空殼子又想出名收割韭菜,先搞小團體,放一些稻草人,再互相取暖。


這也是一種Pattern,叫什麼、什麼神來著?

***

🔲 ☆

鐵帽子王事件的Teddy觀點

September 20 12:30~14:43 

▲先看個貓照片消消火 XD

 

緣起

Teddy明明是一位被鄉民認證過的文人,這幾天卻搞得好像變成武將一樣。Teddy就再講一個文人與水球潘的故事,扳回點文人的形象。

幾年前,水球潘加Teddy FB好友。認識Teddy的鄉民應該知道,Teddy基本上不加沒在真實世界見過面的人為好友,但Teddy加了水球潘。為什麼?因為看到Teddy的指導教授鄭老師,也是水球潘的好友。

Teddy看了一下水球潘公開在FB的基本資料,事隔多年如果記憶有錯請更正,他是銘傳大學畢業,當時正在唸台大資工碩士

Teddy知道鄭老師認識台大資工系的老師,於是自己腦補:「會不會是教授們彼此有交流合作,因此當時還是學生身份的水球潘,才會認識鄭老師?」

Teddy也不會為了這麼小不拉機的事跑去跟鄭老師求證,於是就加了水球潘好友。

這中間,如果沒記錯,Teddy應該沒有跟他有什麼互動。一直到疫情期間的某一天水球潘分享一個Clean Architecture範例。Teddy也在教Clean Architecture,因此下載來參考看看。這是一個很小的專案,Teddy覺得水球潘的Repository設計有可以改善之處,於是留言告訴他。

當時Teddy對於水球潘的反應就覺得有點奇怪,說不上來,有一點他不太歡迎Teddy給他的建議的感覺。

***

第二次接觸

第二次是水球潘在「搞笑談軟工臉書社團」跟Teddy討論Value Object,Teddy覺得水球潘對於DDD Building Block的一些基本認知,從Teddy的角度來看,是錯的。一開始Teddy很有耐心地跟他討論,但後來對於他那種幾乎可以說是詭辯的態度,真的受不了,放棄。

認識Teddy的人應該看得出來,Teddy對水球潘的耐心,已經超過對一般鄉民的耐心。為何?還不是看在鄭老師的面子上,自己腦補說,總是老師認識的學生,就包容一些。

接下來就是今年8/11~13,Teddy在上「Design Patterns這樣學就會了–入門實作班」,有學員跑來問Teddy是否認識水球潘?他用跟Teddy一樣的講法也在教Design Patterns。

 

 

 

這事件,因為「鐵帽子王」事件(水球潘在搞笑談軟工FB社群送給Teddy的幾頂帽子)發生後,Teddy才在FB公開。如果Teddy在意,在八月上課當下就馬上在FB發文了酸水球潘,不會忍到前幾天。Teddy當時笑笑地告訴學員:「我知道這個人,他有加入搞笑談軟工社團,我跟他不熟,印象中是很認真(我知道他有弄社群活動)的人,也很有主見。之前他跟我在FB討論過事情,但雙方看法不同。」

當時問Teddy問題學員,好像有參加水球潘的活動,如果有看到,可以出來佐證。

當時Teddy對於水球潘,一句不好聽的話都沒說,甚至還小小小的誇他。

為什麼?還不是看在鄭老師的面子上。

***

現況

再來就是這幾天發生的事。上禮拜日下午,因為禮拜六剛參加完DDD TW 2023年會,想說在YouTube找看看有沒有更新什麼影片上去,於是下了DDD關鍵字,就看到水球潘的影片。點開一看,一開頭解釋DDD的Design,Teddy覺得基本觀念就錯了。以下是Teddy當時的貼文:

閒來無聊在YouTube找DDD的影片,看到這個影片中說到:DDD的「Design的意思其實就是品質。」
Domain-Driven Design的「Design」不是在講品質,而是「決定邊界」,這是基本中的基本。

我只聽到這裡,後面的內容我就沒聽了。

 

 

水球潘一開始非常強勢,覺得Teddy是「文人相輕」故意找他麻煩。但他卻後來又自己承認,他的說法應該稍加修正。一般人遇到這種情況,就說聲:「噯呀,口誤」,這不就沒事了。

後續水球潘的回應,就是給Teddy扣帽子、潑髒水。更有趣的是,水球潘還自爆內幕:



既然他都自爆他向Teddy「致敬」的內幕,那就不好意思,水球潘已經用完鄭老師面子點數

***

內心話

這中間還穿插一些,從Teddy的角度來看,屬於幼幼班行為的事情,就不提了。

事情演變至此,問題已經不是原本討論關於「Design」定義。Teddy早上去復健,吊脖子的時候在想,以Teddy的身分,在台灣軟工界,不敢說是大佬,好歹也是小佬:

Teddy寫文章、經營泰迪軟體、寫書、辦C. C. Agile社群活動(曾經XD)、經營臉書社群,都已經十年以上,還有最近釋出DDD、DBC與BDD/SBE開源軟體、錄製YouTube頻道等。另外,Teddy的學歷,再怎麼不濟,好歹也是個本土博士(北科大資工)以及北科大兼任助理教授。

Teddy這樣的人,水球潘都敢跑到Teddy經營的「搞笑談軟工社團」來鬧。如果Teddy這次又包容水球潘,日後一般軟體工程師對於水球潘的分享如果有任何疑問,誰還敢發言啊?

看到有些鄉民的思維,有時Teddy都會懷疑:這是在台灣還是在中國?喔,你放在網路上公開廣告的教材內容,不准任何人公開提出疑問?那你不會不要放廣告膩,這樣就沒人會批評了啊!

***

若干年前神收割了一波小白韭菜,很抱歉當時Teddy實力不夠,也不懂神的技術,幫不上忙。但Teddy自己偷偷覺得,應該有小小阻擋了一下神的勢力蔓延到Agile這邊。賣東西卻不允許別人討論產品內容,態度還這麼囂張,這是什麼樣的生意可以這樣?想想這些轉職者與技術小白,真的是挺可憐的,想要努力變好,還要被騙。有種海龜寶寶破殼而出,努力爬向大海的途中,還有一堆海鳥等在旁邊要吃他們的即視感。

***

友藏內心獨白:我不入地獄,誰入地獄。

🔲 ☆

改行寫網路小說算了(24)

September 19 16:10~18:50

 

炸神誕生

約莫2500年前,羅剎國的土地上出現了一位傳說中的神級人物。其出生地、父母、生日與姓名均不詳。因成年後的職業為農夫,且養馬,江湖人稱馬農

馬農雖為農夫,但並不耕作,專以收割別人土地上長出的韭菜為生。因為騎馬,跑得快,上面坐個老太太 眾多受害農夫想抓馬農久已,可惜連他的馬尾燈都看不到,反倒吃了滿嘴灰。於是農夫們集體討論想出一個辦法,準備很多鞭炮,當馬農出現時,用鞭炮炸他身體,把他轟下馬。

某日,馬農出現在羅剎國的霄霄村,準備再次收割別人農田中的韭菜。就在此時,一位正好在田邊樹陰下大便的農民發現馬農。農夫顧不了WIP,稍微施力剪斷大便,立馬讓WIP歸零。農夫緊接著來到工作間拿出準備以久的鞭炮丟向馬農,並大喊:「炸他身體、炸他身體」。無奈,馬農騎術優良,能打勝仗,一轉眼就消失在樹林中,隱姓埋名,從此江湖中再也無人見過馬農。此後,農夫們改稱馬農為炸身---炸他身體的簡稱。炸身、炸身,農夫們希望喊久之後,下次再見到馬農,可以炸他身體,一炮斃命。

若干年後,炸身的傳奇故事,傳來傳去,不知怎麼地居然被神格化。炸身被尊稱為炸神,持續保有一群忠實信徒,不離不棄。

***

主角小刀

本故事主角,小刀,是村民所認證的三字經知識權威,以三字經教學為生。小刀平日閉門在家讀經,鮮少涉足農田,自然不知曉炸神這號人物。小刀第一次看到神的名字,是在「小刀讀經班」的課堂上。

消失多年的神,再次下凡化身為一位普通書生。神在課堂上的表現,並沒有特別引人注意。上課當時,小刀有眼無珠,還不知道眼前這位平凡書生,就是大名鼎鼎的炸神(奏樂,噹噹)!

在課堂上的經文討論活動中,神總是特立獨行。不是默默不語,自顧自的下著他帶來的一盤大旗(當下流行的一種虛擬挖人礦桌遊),就是大言不慚地發表一些驚世駭俗的言論。難道是神覺得小刀的三字經講解的不到位,所以故意搗蛋引發關注嗎?好像也不是這樣,因為神還報名了一次夜間讀經活動,應該是對小刀的三字經有信心才是。神在課堂上的表現實在是太平凡了,完全沒有神該有的樣子,小刀一直以為神就是個普通書生,還好心告訴神一些關於讀經的建議。

直到多年後,才有高人在夢中告訴小刀,當年那位在課堂上不起眼的書生,其實就是神,大名鼎鼎的炸神!

***

所有的大神都有一個共同特點,就是具備一擊必殺的神邏輯。神用神的視野「發現」平常人老早就已經知道的東西,並將其宣稱為神發明。例如,神再次發現了火,可以煮飯;水,可以喝;屎,可以施……….肥。多麼了不起的的發明啊!一般人類,是無法用人邏輯與神溝通。因為,神,活在神界!

神邏輯還可以不必遵守人間一切物理定律。

神說智慧,就有了智障

 

不可以質疑你的神!

***

有人說,神的口才很好,擁有大批信眾。每逢初一、十五,信徒提供大量韭菜供養神,讓神過著優渥的生活。關於神的好口才這個傳聞,小刀無法證實。因為在短短兩次聆聽神諭的機會中,化身為書生的神,是那麼的低調。涉世未深的小刀,著實體察不出神的嘴砲神功之威力。

有了信徒的供養,神此後不用再騎馬割韭菜。神賣掉了馬,出門改搭屋駁,一種有蓋子的轎子,負責在霄霄村內短期通勤之用。

又過了幾年,神再度轉趨低調,眾多信徒們已有多年沒有神的消息。

***

神蹟再現

此刻,神再次降臨。本番下凡,雖然神的性別不明、長相不同,但能力依舊。

神在下凡時大喊一聲:

萬能的天神,請賜與我葛雷堡神奇的力量!

神說獨創,就有了模仿

可憐,凡間

小白花盛開的田裡

韭菜塗炭

***

後人寫詩一首以茲紀念:

智慧曰智障

獨創喚模仿

施展神邏輯

引燃自爆彈

***

友藏內心獨白:橫批--集體智障。

🔲 ⭐

創業初期的陳年往事

September 19 05:01~06:17

 

▲就好像吃飯睡覺一樣,還不是你做什麼他也做什麼

 

緣起

上周六Teddy參加了DDD TW 2023年會活動,禮拜天下午閒來無事在YouTube上找DDD的影片來看。滑到某個影片,沒想到因此發生了一連串讓人哭笑不得的事,細節Teddy就不重複描述,今天想聊一件陳年往事。

***

永遠的泰迪之友

十年前泰迪軟體剛成立沒多久,有一位YA先生主動找Teddy聊天。YA先生早年也經常出現在「搞笑談軟工部路格」文章,當時他在新創公司上班,遇到一些問題。因為敏捷開發是當時泰迪軟體的主要業務,YA先生想了解導入敏捷開發能不能對他的工作有所幫助。

YA先生是一位非常熱心的人,幾年前他去了德國工作,是Teddy心目中永遠的泰迪之友。因為YA先生是泰迪軟體成立之後第一位找Teddy「諮詢」的客戶,Teddy當時也是剛創業的菜鳥一隻,雙方約在北科大對面的伯朗咖啡聊聊天。Teddy並沒有跟YA先生收費,只是讓他請了一杯飲料。可能是因為這樣,YA先生覺得過意不去,好心找Teddy去跟他們公司的新創顧問談一談,看看對Teddy的創業有沒有什麼幫助。

***

Teddy記得對方是一位長相帥氣的年輕ABC(以下就以ABC稱呼這位先生),對話的時候英文、中文交錯使用,好像在美國某創投公司待過。由於年代久遠,細節Teddy已經記不清了。ABC聊到創投的一些生態,他說以前他們常常看很多新創的案子,因此衍生出一些快速篩選案子的原則。就好比大公司HR每天可能收到幾百份履歷,總不可能每份都逐一閱讀,因此用學歷、畢業學校作與工作年資為條件,快速過濾掉不符標準的求職者。

ABC:有一種case,就算是business model再棒,我們絕對不會投。

Teddy:為什麼?

ABC:因為非法!

ABC:我們看過一個case,公司計畫從YouTube下載各種影片、音樂,然後再提供服務給第三者。

ABC:侵權、非法的case,就算他們現在活耀的使用者數量再多,我們都不考慮,這是底線。

Teddy原本以為新創公司因為新的業務模型,遊走在法律邊緣是很常見的,不然當年YouTube一開始營運上面不是有很多非法的影片。不知道是這位ABC待過的創投比較不一樣,還是這是一個業界標準,當時Teddy也沒多問。

***

結論

老美對於做生意不可剽竊他人智慧財產這件事,相對而言還是比較嚴肅看待。在美國念書,如果作業被抓到抄襲,是一件很嚴重的事情。

抄襲者為什麼要抄襲別人?因為可以彎道超車,自己不用花很多時間,就可以看起來好棒棒。抄襲的過程中,不只被抄襲的人受害,消費者也受害。

先幫「小白」默哀三秒!

***

友藏內心獨白:用新的謊去圓舊的謊,就容易自爆。

🔲 ☆

買到還不錯用的筆電收納架

July 04 18:48~19:22


  

▲圖1:把兩台筆電立起來收納

前言

Teddy有兩台筆電,第一台是2018年從Amazon買的LG gram 15,這台筆電非常棒,很輕續航力又強,這的是沒什麼好挑的。唯一的問題是年華老去,速度有點跟不上,去年不知哪根神經不對勁,居然買了Asus UX5401Z。Asus 這台筆電,才14吋但體感重量比起LG gram 15,有種沉甸甸的感覺。如果是買水果沉甸甸也就算了,買筆電沉甸甸真的不優。

不過今天不是要談筆電,而是要談筆電收納。Teddy平常在家中工作,使用的是2019 iMac 27”,筆電只有外出上課時才會用到。這兩台筆電,平時就被Teddy丟在不同的位置,平躺著有點占空間。前天晚上在PChome上面隨意選了「YUNMI 重力感應筆電直立式收納支架」,收到之後沒想到還不錯用。今天介紹這個支架,提供有需要的鄉民做為參考。

***

非廣告


 ▲圖2:PChome的產品介紹畫面

 

502元,價錢還可以。Teddy以前曾經想買一個筆電收納架放Apple Air與MacBook Pro,但後來換了iMac之後就作罷。這次是整理書桌,順便把找個架子收納筆電。看到這個產品,可以收納多台筆電,抱著可能採雷的覺悟,就買了。

 

▼開箱之後就一個塑膠做的架子,長成下面這樣。


  

▼準備放下兩台筆電,還沒放到底部,此時筆電架還沒有夾緊


▼放到底部之後,筆電架就將兩台筆電夾緊。

   

▼空拍圖


***

結論

▼收納之後將筆電放在書桌後方,留一條Type C線讓兩台筆電輪流充電,還行。

 

***

友藏內心獨白:可能因為難得沒採雷所以特別高興。

🔲 ⭐

為什麼Teddy沒使用Specification設計模式?

June 14 21:46~22:53


  ▲圖1:定義Specification介面


前言

在6/5~6/7去客戶家上【領域驅動設計與簡潔架構入門實作班】的時候,有一位學員問Teddy:

  1. 為什麼Teddy建議Entities Layer的物件不要直接操作Repository?
  2. 為什麼有人說UoW(Unit of Work)和Repository在DDD裡面算是Anti-Pattern?
  3. 為什麼Teddy沒有用Specification模式?

前兩集談了前兩個問題,今天討論最後一個問題:「為什麼Teddy沒用Specification設計模式」」?

 

***

Specification 設計模式

這一個設計模式是由Eric Evans與Martin Fowler所整理的,可在此下載介紹該模式的pdf檔案

Specification這個名字很容易讓人聯想到規格或是需求,但它的用作其實是Filter(過濾器)Selector(選擇器)。傳統上,開發一般CRUD-Based的系統,開發人員很常直接下SQL操作資料庫去尋找所需的資料。但是在領域驅動設計(DDD)中,強調透過領域模型來表達業務邏輯。「尋找符合條件的領域物件」這件事,本身就是一個業務邏輯,因此在領域模型中應該有相對應的物件來表達這樣的業務邏輯。而Specification設計模式就是為了這樣的應用場景而存在。

實作Specification很簡單,它的介面只有一個方法isSatisfiedBy,請參考圖1。isSatisfiedBy接受一個物件(通常是領域物件,例如Board, Workflow, Card這些物件),如果物件的內容滿足concreate specification所指定的條件,則回傳true,代表這個物件「符合規格(選到這個物件了)」。

***

圖2是以個用來選擇Board是否屬於某個Team的規格,稱為BoardBelongToTeamSpecification。



▲圖2:BoardBelongToTeamSpecification範例程式 

 

圖3為BoardBelongToTeamSpecification的使用方法,首先呼叫getBoardLise()產生四個Board。然後產生BoardBelongToTeamSpecification instance,傳入”Team 1”當作查詢條件。接著用BoardBelongToTeamSpecification 當作過濾條件,從這四個Board裡面選出Kanban Board與Board Game。


▲圖3:BoardBelongToTeamSpecification使用範例 

 

在Eric Evans與Martin Fowler所整理原始的文章中,Specificatoin還可以串接,形成更複雜的選擇規範。

***

為什麼沒用Specification?

由上面例子可以看出來,Specification其實就是一個Predicate。從實作面的角度來看,可以直接用Lambda來完成。Specification之所以存在,還有一個很重要的作用力,就是要重複使用這個業務邏輯。你可以用Specification去資料庫中挑選資料,或是驗證領域模型物件是否符合否項業務規格。在這種情況下,如果使用Lambda來實作,就會產生重複程式碼。

寫到這裡,還是沒講為什麼在ezKanban中並沒有使用Specification。原因如下:

  • ezKanban透過撰寫合約的方式來驗證領域物件的正確性,而非使用Specification。
  • 如果要透過Specification去資料庫挑選資料,那麼資料必須先從資料庫中載入記憶體或是用某種映射的方式,把傳給isSatisfiedBy()方法的物件身上的每一個欄位,去匹配資料庫中的欄位。因為ezKanban支援State Sourcing與Event Sourcing,無法光用傳統State Sourcing的方式如果採用Specification去資料庫比對資料。因此,ezKanban就沒有使用Specification,而是針對不同儲存方式與不同資料庫,用資料庫相依的方法,撰寫特別的查詢物件。
  • ezKanban套用CQRS,而CQRS和Specification是兩種互相衝突的設計模式。詳細原因請參考Vladimir Khorikov的部落格文章 <CQRS vs Specification pattern>。

***

結論

Teddy當年在學DDD的時候,印象中只有在DDD藍皮書中看到Specification的介紹,在比較接近實作的DDD紅皮書與DDD橘皮書中,沒什麼印象提到Specification。可能是Teddy學藝不精,所以開發ezKanban的時候潛意識中就沒有套用Specification。

但Teddy目前沒用也不代表以後不會用,反正這麼多設計模式,多了解一點也沒什麼不好。等到哪天有合適的場合,這些模式會自動跑出來報效國家。

***

友藏內心獨白:用Collection操作資料還是非常方便滴。

🔲 ☆

使用ezSpec落實行為驅動開發與實例化需求(8):規範平行行為

March 24 21:45~23:14

▲圖1:電梯的規格範例

 

前言

ezSpec與Gherkin相關的基本功能已經介紹完畢,今天介紹一個Gherkin沒有的功能:「描述平行行為的規格」。

Gherkin的Given, When, Then, And, But這些Step依據它們出現在Scenario的先後順序依序執行,對於一些天生就具有平行處理能力的系統,例如在IoT(Internet of Things)系統中,多個sensor或device彼此之間都是獨立且平行執行。在這種情況下,用Gherkin就無法表達這些平行執行的行為。

三月初的時候實驗室一組研究IoT的學生跟Teddy介紹他們用python開發的工具—concurrentSpec,它擴充Gherkin語意讓開發人員撰寫並執行同步行為規格。原本Teddy開發ezSpec並沒有計畫要支援描述平行處理行為,聽完學生的介紹,覺得加上這個功能可以讓描述行為的語意變得更完整,因此花了點時間在ezSpec中支援這個功能。

***

平行運作行為的範例

請參考圖1,該例子節錄自 <Specifying Internet of Things Behaviors in Behavior-Driven Development: Concurrency Enhancement and Tool Support> 這篇論文,論文作者是實驗室IoT小組的成員以及Teddy的指導教授鄭老師。論文中提到一個描述電梯規格的例子,這個例子參考自 Jackson所寫的兩本書:《Software Requirements & Specifications: A Lexicon of Practice, Principles and Prejudices》與《Problem Frames: Analysing and Structuring Software Development Problems》,圖1的中文敘述是請ChatGPT幫忙翻譯。

這個例子如果用標準的Gherkin執行,假設第29行執行失敗(無法打開緊急指示燈),測試案例就會停在第29,後面的assertion就不會被驗證。在電梯的例子中,這種行為很顯然是不正確的。因為就算29行與30行都失敗,只要第28行成功(電梯有停在最近的樓層),第39行就應該被檢查(電梯門要在五秒內打開)。

有些測試框架可以讓使用者設定:「就算某一個assertion失敗,測試案例還是持續執行」。但就算是採用這種方式來執行圖1的例子,最後結果還是可能錯誤。例如第28行失敗但第31行成功,這表示:「電梯沒有成功停在最近的樓層,但是電梯門最後卻打開了。」這顯然不是使用者所期待的行為。

怎麼用Gherkin描述同步行為?concurrentSpec提出一個很簡單的擴充方式:「在不增加Gherkin keyword的前提之下,將Given, When, Then當作同步執行群組的起頭,它們之後的And與But將會與它們同步執行。整個同步群族執行完畢之後,才會開始執行下一個同步群組。」換句話說,Given, When, Then彼此之間是循序執行,但它們之後若接著And或But,這些And/But將與它們平行執行。

此外,還可以指定每一個Step如果執行失敗,之後的Step是否要繼續執行。

***

用ezSpec描述圖1行為的程式如圖2所示,Teddy故意讓「打開緊急指示燈」與「取消該叫車請求」發生錯誤(839行與842行),但是這兩個Step如果發生錯誤,下一個Given/When/Then依然會繼續執行(因為指定ContinuousAfterFailure參數)。請注意,圖2中的Step執行順序如下,Given, When, Then, Then是循序執行,第一個Then與後面兩個And為平行執行。

  • Given
  • When
  • Then, And, And (834、838、841行):三個Step平行執行
  • Then

圖2中有一個小細節要注意,就是要讓Scenario以平行的方式執行,必須呼要ExecuteConcurrently()


▲圖2:用ezSpec描述電梯規格範例

 

圖3顯示圖2執行結果,可以看到兩個And執行失敗並沒有影響後續Then的執行。


▲圖3:圖2執行結果報表

***

電梯門打不開

Teddy修改一下電梯的Scenario,故意讓電梯沒有停在最近樓層,請參考圖4。





▲圖4:模擬電梯沒有停在最近的樓層。

 

修改後的Scenario執行結果如圖5,可以看到因為Then沒有加上ContinuousAfterFailure參數,所以整個同步執行群組執行完畢之後,就不會繼續執行下一個Step。


▲圖5:圖4執行結果報表

***

看到這裡鄉民們可能會想:「不管電梯有沒有停在最近樓層,我就是要執行最後一個步驟的檢查啊!」也可以,修改一下Then,幫它加上ContinuousAfterFailure,請參考圖6。

 

▲圖6:加上ContinuousAfterFailure參數,即使電梯未停妥也繼續執行後續驗證步驟

 

修改後的Scenario執行結果如圖7,如果系統行為真的如此,恭喜你,找到一個bug。什麼bug?電梯未停靠最近樓層,但電梯門卻打開了。


▲圖7:圖6執行結果報表

***

 

結論

如果鄉民們的系統與IoT系統類似,有著很明顯的平行行為,那麼標準的Gherkin語意就無法描述這些行為。借用concurrentSpec所擴充的Gherkin語意,可以在不增加Gherkin keyword的前提之下,使用ezSpec描述平行系統的行為。

ezSpec指定行為的功能差不多介紹完畢,下一集介紹ezSpec產生報表的功能。

***

友藏內心獨白:快到山頂了。

🔲 ⭐

使用ezSpec落實行為驅動開發與實例化需求(6):Scenario Outline

March 21 18:06~19:30

▲一個Test Method,N個綠燈

 

前言

上一集介紹在ezSpec中四種撰寫Scenario Outline的方式(請參考<使用ezSpec落實行為驅動開發與實例化需求(5):撰寫Scenario Outline的四種方法>),所使用的Examples是比較簡單的單一表格。這一集介紹當Scenario Outline的Examples比較複雜的時候,該如何表達這些Examples。

***

JUnit 5的ArgumentsProvider

在說明如何表達複雜的Examples之前,先介紹兩個基本的介面/類別:ArgumentsProvider與Junit5Examples。

JUnit 5 的@ParameterizedTest原本就支援好幾種提供測試資料的方式,上一集使用最簡單的@CsvSource,這一集要用ArgumentsProvider來提供測試資料。參考圖1,ArgumentsProvider是一個介面,只有一個provideArguments method,回傳Stream<? extends Arguments>。

Arguments也是一個介面,參考圖2,它身上有一個get mehtod,回傳 Object 陣列,這就是存放參數的地方。

 

▲圖1:JUnit 5的ArgumentsProvider 介面

 

▲圖2:JUnit 5的Arguments介面

***

ezSpec的Junit5Examples

ArgumentsProvider是JUnit 5 設計給的@ParameterizedTest使用的介面,ezSpec的Scenario Outline接受的參數是Examples,兩者介面不同。因此Teddy設計一個Junit5Examples抽象類別,透過它可以轉換ezSpec的Examples以及 JUnit 5的ArgumentsProvider,如圖3所示。

 

▲圖3:ezSpec的Junit5Examples抽象類別

***

 

RenameWorkflow Use Case的Scenario Outline

參考圖4,假設你要幫ezKanban的RenameWorkflow使用案例定規格,你想到兩個主要的Scenarios:

  • Scenario 1:新的名稱與舊的名稱不同,執行RenameWorkflow使用案例之後會產生一個領域事件。
  • Scenario 2:新的名稱與舊的名稱相同,執行RenameWorkflow使用案例之後不會產生領域事件。

針對這兩個Scenario,你一共找出五種不同的測試資料,例如把Workflow的名字從dev改成deV、從dev改成DEV,或是從dev改成dev。

 

▲圖4:Rename a workflow 範例

 

圖4的Scenario Outline,要如何用ezSpec來表達?首先定義測試資料,請參考圖5與圖6。

 

▲圖5:不同Workflow名稱的測試資料(Examples)

 


▲圖6:相同Workflow名稱的測試資料(Examples)

 

準備好Examples之後,就可以在Scenario Outline中使用它們。參考圖7,採用上一集所介紹的方法一來撰寫Scenario Outline,唯一不同處在第61行:

WithExamples( Junit5Examples.get(different_name_examples.class),

                          Junit5Examples.get(same_name_examples.class))

直接指定Examples的class name來獲得測試資料。

 

▲圖7:使用Junit5Examples的具體類別當作測試資料來源提供給ezSpec

 

另外,different_name_examples與same_name_examples也可以作為@ParameterizedTest的資料來源,用來執行Scenario Outline的,請參考圖8。


▲圖8:使用Junit5Examples的具體類別當作測試資料來源提供給@ParameterizedTest

***

更複雜的測試資料:表格中還有表格

請參考圖9,這是提供給ezKanban的MoveLane使用案例的驗收測試資料,其中givenWorkflow表示在Given階段初始化的Workflow狀態,expectedSubStageAsRootStage表示移動Lane之後Workflow的狀態。

 

▲圖9:Examples包含表格的範例

 

MoveLane的Scenario Outline如圖10所示,可以看到在89行指定了<given_workflow>參數(圖9中的givenWorkflow表格),以及在123行指定了<expected_workflow>參數(圖9中的expectedSubStageAsRootStage表格)。


▲圖10:MoveLane Scenario Outline範例

 

圖10的執行結果如圖11所示,可以看到第22是最外層的Examples表格,其內部的given_workflow與expected_workflow這兩個column各是另一個表格。可以透過ezSpec很簡單的設計複雜的驗收測試資料,使用上也很簡單。


▲圖11:MoveLane Scenario Outline執行結果報表(只顯示部分)

***

結語

透過ezSpec的Junit5Examples可以設計同時讓ezSpec與JUnit 5的ParameterizedTest所使用的驗收測試資料,每一種測試資料獨立定義在一個Junit5Examples類別中,容易管理與撰寫。

寫到這裡,常用的ezSpec功能已經介紹完畢。下一集介紹Background,可以用來將相同的Given寫在一起,以利重複使用與減少重複程式碼。

***

友藏內心獨白:Table內的Table,還是Table。

🔲 ☆

使用ezSpec落實行為驅動開發與實例化需求(3):撰寫Scenario與傳遞簡單參數

March 18 20:27~22:12;March 20 00:00~00:30



前言

介紹完ezSpec的領域模型(請考<使用ezSpec落實行為驅動開發與實例化需求(1):領域模型介紹>)與Feature和Story(請考<使用ezSpec落實行為驅動開發與實例化需求(2):Feature與Story>),終於要進入主題,今天介紹如何用ezSpec撰寫Scenario。

***

撰寫Scenario

假設你要開發一隻開發票的程式,可以從未稅金額計算含稅總價與稅金,你幫這隻程式寫的第一個Scenario如圖1所示:「當一台電腦的未稅金額為20000,營業稅為5%,當你買了這台電腦,你應該支付含稅金額21000的總價,其中1000元為營業稅。」

產生Scenario的方式很簡單,透過feature.withStory()找出事先建好的Story(請考<使用ezSpec落實行為驅動開發與實例化需求(2):Feature與Story>),然後呼叫Story身上的newScenario就可以產生一個新的Scenario。在產生Scenario的時候你可以指定這個Scenario的名字,如果未指定則ezSpec會自動抓取test method的名字當作Scenario的名字。

產生Scenario之後,可以透過它的Given、When、Then、And、But(統稱為Step)等method來撰寫Scenario的實際內容。每一個Step接受兩個參數:

  • 字串:用來描述Step內容的文字敘述。
  • Lambda:實際實行Step的程式,ezSpec使用Lambda來取代Cucumber的Step Definition。

由於本系列文章的主要目的是介紹ezSpec的使用方式,並不是要介紹透過TDD/BDD/SBE來開發軟體,所以接下來Teddy會說明撰寫Scenario的時候如何指定參數、讀取參數,以及傳遞參數的方式。

 

▲圖1:ezSpec的Scenario範例

***

指定與讀取無名參數

在圖1的範例中,一共有四個參數:

  • 第61行的未稅金額20,000
  • 第64行的營業稅5%
  • 第69行的含稅總價21,000
  • 第72行的營業稅1,000

有兩個方法可以在Step的Lambda中讀取這些參數,第一種方法是在這些參數前面加上$符號,Step的Lambda可以傳入一個ScenarioEnvironment的物件當成參數,可以透過ScenarioEnvironment在Lambda中讀取$開頭的字串,請參考圖2中的env參數。

 

▲圖2:ezSpec指定與讀取無名參數的Scenario範例

 

圖2示範三種讀取參數的函數:

  • 第62行evn.getArg:回傳String型別的參數
  • 第65行evn.getArgd:回傳double型別的參數
  • 第70行evn.getArgi:回傳int型別的參數

透過$方式所指定的參數,只有value,沒有key,因此在讀取時需透過index方式來讀取資料。在圖2中每一個Step剛好都只有一個參數,因此透過env.getArg(0)就可以拿到這些參數。

***

指定與讀取有名參數

既然有無名參數,就有有名參數,請參考圖3:

  • 指定有名參數:參考圖3的61行可用 ${tax_included=20,000},或是用第64${vat_rate:5%}來指定參數的名稱(可用=或:)。
  • 讀取有名參數:參考圖3第62、65、70、73,讀取方式和圖2類似,但此時使用字串的key來讀取參數內容。

 

▲圖3:ezSpec指定與讀取有名參數的Scenario範例

***

讀取其他Step的參數

有時候你想要在Lambda中讀取其他Step所定義的有名參數,這時候就要用getHistoricalArg函數來取得,請參考圖4第74行:env.getHistoricalArg("tax_included") 讀到第61行所定義的tax_included參數。

 

▲圖4:ezSpec讀取其他Step的有名參數範例

***

在不同的Step中傳遞資料

在撰寫Scenario的時候經常需要在不同的Step之間傳遞資料,例如在67行的When當中你用hasBought變數代表成功購買到電腦,你想在Then的Lambda中驗證hasBought的內容。請參考圖5,此時可以使用ScenarioEnvironment的put(key, value)將要傳遞的資料放到ScenarioEnvironment(第69行),然後在另一個Lambda中用get(key, Class<T>)將資料讀出(第73行)。

 

▲圖5:ezSpec在不同的Step中傳遞資料

***

結語

今天介紹ezSpec撰寫Scenario與指定和讀取參數的方式,Scenario也可以接受一個Table的資料當作參數,Teddy將在下一集介紹這個功能。

***

友藏內心獨白:用Lambda撰寫Step Definition就不用處理煩人的正規表示式了。

❌