---
title: 單元測試課程共筆
---
# Day 1
### 前言
##### 一、fizzBuzz小遊戲
```
Q. y = f(x)
input x: number
output y: string
if x 為 3的倍數, output 輸出 "fizz"
if x 為 5的倍數, output 輸出 "buzz"
if x 為 3 & 5 倍數, output 輸出 "fizzBuzz"
if x 有包含3, output 輸出 "fizz"
if x 有包含5, output 輸出 "buzz"
其餘 output 輸出 "x"
```
##### 二、代碼展示與說明
###### 查詢目前是否有申購方案(/extMemberPackage/list)
問題點
1. HttpServletRequest/HttpServletResponse 不要直接帶入於function param
```java=
public String example1(HttpServletRequest request, HttpServletResponse response, String UID) {}
```
2. 變數命名看不出意義
```java=
JSONObject result = new JSONObject();
```
3. 靜態方法,應在中間層做掉
4. error handle / logger 應透過AOP方式或其他方式,而不是寫在controller/service
5. 透過apiResponse物件/介面 統一response回傳格式
##### 三、維運 tips
- 使用測試帳號 + 定時任務做 production end-to-end 測試
- 萬一回應異常,即時做到通知 RD ,或是在首頁顯示服務異常,才不會讓客戶覺得產品難用、客服一問三不知
##### 未分類
- 面對較複雜的文件時(例如 package code),適時附上範例,讓讀者容易進入 context (這也可當作測試情境)
- 申裝 Flow 建議: 除了參數的檢查,防呆檢查也在前段(邊界層)先行處理、攔截,不要讓髒資料進到系統中後段,測試也會更好寫
- 設計 API 時,若參數同名,應該做區分,例如 exteralOrderId vs orderId
- 遇到自己負責的產品,應改掉"需要看程式碼、DB才知道產品在幹嘛"的習慣
- Service 層不應回傳 json/String,應回傳"看得出意義"的物件或DTO,否則單元測試會變得很難驗證,還要做序列化等雜務
- 單一職責原則的另一種解釋: 需求異動時,應只會改到一個地方。(TDD 祖師爺 Kent Beck 的一篇文章寫"單一職責"寫得很好,一開始做規劃與設計時,非常難界定單一職責,或是界定完之後很容易需要改變。假設一個需求異動要改三個地方,能不能把這三個要改的地方,放在一起,改一個地方就好,就不至於跨三個物件。(Layer 不一樣,Layer 軟性的把架構等級的層次拉出來)。假設 A service call B service call C serivce,需求異動時,都需要改到程式,有可能代表您將同一個職責拆分到三個 Class 裡面,應該將其合併。否則下次異動你仍需要改到好幾個地方。)
- 單一職責的反模式 : 把一個職責拆成太多物件,用的人必須協作那幾個物件,此時,就要考慮把相關的東西再聚合在一起。
- 敏捷,先寫主幹,以情境為單位。先不管 input 檢查、error handling,快快給 user reivew,因為 user 可能會跟你說,原本這些都不要了。
- 變數生命週期越短越好 : 變數的宣告距離使用點越近越好。
- 還技術債的第一步是不要再欠債。
- 寫測試程式發現窒礙難行,應該是原始的程式碼有問題。
- Product 是沒有 requirement 的,而是有 Features,指的是,使用者想達到的真正目的,目的一種,但可以有很多種作法。eg. 賣電鑽 ? 還是牆上的一個洞 ? 開發者可以把自己往前推,到產品的角度看事情。
- 工程師可以比其他人更有商業價值的概念,知道怎麼要將 features/scenario 排序。
- Service Provider : 需要很在乎使用端如何使用,才知道如何提供服務,才知道好不好用。不然,很容易只有看到一個 entry point。把自己定位會做產品,才能考慮使用者和商業價值,這件事情很重要,但慢慢來。後面往前面擴展,對您的職涯很重要。
- production 上如何做測試 : eg.想知道客人申裝方案有問題,此路不通,我希望三分鐘內就知道。(可以寫個 batch 不斷地使用它,用測試使用者、且容易re-cycle。在克服來電之前,就已經知道且已經在查了。
- 正式環境要有測試帳號,進入系統需要被濾掉。
- 中間過程中,避免用 JSON(序列化),JSON 看不出來要表達的意義。過程中常常要處理 JSON,真的很麻煩。
- method 的參數傳入覆寫掉是很糟糕的作法。
- 宣告初始值=NULL,大概都還有改善空間。
- 鼓勵前後端坐在一起,(或是 designer 與 developer 以是這樣),可以好好講,不坐在一起,就會抱怨彼此。
- 人永遠不夠、時間永遠不夠、需求永遠在變,在既有資源下交付最大價值,是 proudct mgt. 要思考的點。
- 一個 function 很複雜,但我們通常用情境來看。整包程式碼很複雜,需要用一些情境來整理。
- 有關於API reqeust/respone parameter 的設計,避免因為方便,共用回傳的 data schema (applyId :0 代表查詢可以安裝的方案; applyId: 1 代表縣在身上的方案),可能會因為 response 的 data schema 誤解這個 API 的意義。
- API input 參數的 naming,不要造成 confuse,eg. orderid vs. supplier-orderid。
- API response 的設計,一次給足相關資訊,避免多次網路來回。
- 參數名稱避免使用縮寫 : eg. ext
- 檢查不要寫在 Controller,可以放在中間層,像是 decorator (在外層統一做 error handling,exception handling),只寫一次 log。所有程式碼都是正常的,異常的話,拋出去,這樣寫 TDD 就很簡單。
- 敏捷 : 先寫主幹,不要管 input 檢查、error handling,快快給 user review,user 可能會說,原來這個不要了。
- Encode/Decode 在中間層做到
- 宣告變數離用的地方越近越好
- 變數的生命週期越短越好
- Method 傳入的參數被複寫掉,是否糟糕的做法。
- 避免 return NULL,不然呼叫端一定要做檢查。
- 還技術債的第一步是不要再欠債。
- 解密放在method的開始很有問題,要寫測試程式就很困難,因為要先產生一個密文。
- 寫測試程式覺得窒礙難行,一定是原始的程式碼有問題。
- PM 希望 10 items 半年內做出來,IT 可以建議取其中 3個MVP item 一個月內做出來推到市場讓老闆開記者會,後面的 7 個 items 加上這一個月,或許可以比原來的半年還長,但反而可以被接受。
- 工程師可以比其他人更有商業價值的概念,知道怎樣將 Features/Scenario 排序。
- 如果有一個method我想要記得等一下回過頭要記得改,可以把自己名字放到 method naming 中。
- 過程中常常要處理 JSON 物件,真的很麻煩。
- 宣告初始值是 NULL,大概都還有改善空間。
- 從頭到尾,Contorller 就是叫別人做事情,不需要知道怎麼加工,sequence diagram 畫出來,與注入的物件互動,不要知道很多過度時間的資料,改成叫 service 自己去做事情就好。(進進出出的次數變少、變得越來越簡單、能做的事情一樣多)
### 參考資料
#### Windows 剪貼簿
https://support.microsoft.com/zh-tw/windows/%E5%89%AA%E8%B2%BCwindows-c436501e-985d-1c8d-97ea-fe46ddf338c6
#### Decorator
https://springframework.guru/gang-of-four-design-patterns/decorator-pattern/
#### Java Spring Controller Exception AOP
:::success
:bulb: **提示:**
Spring ControllerAdvice 提供統一處理Controller 錯誤
```java
@ControllerAdvice
public class ControllerAdvisor extends ResponseEntityExceptionHandler {
@ExceptionHandler(CityNotFoundException.class)
public ResponseEntity<Object> handleCityNotFoundException(
CityNotFoundException ex, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("message", "City not found");
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
}
```
:::
#### primitive obsession
https://refactoring.guru/smells/primitive-obsession
#### simple design
https://explainagile.com/agile/xp-extreme-programming/practices/simple-design/
#### feature envy
https://refactoring.guru/smells/feature-envy
```tiddlywiki=
重構技巧 Move instance mthod
```
# Day 2
### 前言
:::info
- #### 試著使用反例激盪出正向的需求input
:::
### vim
```
viB 選擇括號內所有內容
alt + / -> expand cyclic(打關鍵字,會自動搜尋)
zt
zz
zb
```
### idea
```
shift + shift 可以查各樣功能快捷鍵 (extract method 、 inline 、 introduce var)
Recently Edited : ctrl + shift + E
Refactor this : ctrl + alt + shift + T
Extract method : alt shift M
inline : alt shift i
instance method : alt shift v
Rename All : alt shift v
surround with : ctrl + alt +t
跳到括號的開頭與結尾: ctrl + shift + m
```
#### Specification by example
https://en.wikipedia.org/wiki/Specification_by_example
#### 穀倉效應
https://www.cheers.com.tw/article/article.action?id=5099818
#### Breadcrumbs
https://en.wikipedia.org/wiki/Breadcrumb_navigation
#### Java Double Brace Initialization
https://howtodoinjava.com/java/double-brace-initialization-in-java/
#### Tell, Don't Ask
https://kaisheng714.github.io/articles/tell-dont-ask
#### 未分類
- 剛好的設計,不太可能一開始就做到,通常是透過重構的過程,否則通常是 over-designed。
- 回答讓別人聽得懂,這也是軟體開發的一環。
- 需求釐清會議,最後的產品應該是一條一條的情境;不應該是跳過這些情境,直接給出 table schema 給開發人員。QA (測試左移)也要在場,整個團隊都要在場,特別是接近使用者的。
- 需求釐清會議(實例化需求) -> Pair Programming -> Code Quality Review -> 給另一個開發者測試 -> 給 QA 測試 (探索性測試,使用者亂弄看會不會壞掉)(有一定的默契、QA 不需要再去測試 programmer 測試過的)。
- 角色之間需要協作,不然會有很多的浪費。
- Code Review : 真正去看是一回事,但知道有人會來看,至少不會亂寫程式。
- Code Review : 老師會換一個說法,改稱為 : "代碼欣賞",是 "團隊的養分";因為,有人願意貢獻程式碼,對團隊開發的風格產生一致性。
- Code Review vs. Pair Programming : Code Review 類似於 auditor,屬於指指點點;但 Pair Programming 是一起做、共同產出。
- 設計的概念 : "Tell, Don't Ask ! "
- Domain Model(object):減少外部依賴,自己完成事情,把結果存在自己的肚子裡面。
- Domain object 的設計,會不斷地重構,例如 範圍太大了,需要切開。
- Doamin Object 有時候是透過對 Legacy Code 重構抽取出來的。這時候的內容都是有用到的;有時候一開始就想盡辦法設計完整一個 Domain Object,反而容易 over-designed。
- 剛好的設計 : 不可能一開始就做得到,通常是透過重構的過程,否則通常容易 over-designed。
- Code Smell : Method 名字太長,或參數太多,有時候蘊藏著問題,可能是這個 Method 想要做的事情太多且相關性不那麼高。
- 時間永遠不夠、人永遠不夠、需求一直會變;如何在既有資源下交付最大的價值,這是要思考的點。
- 善用工具(eg.IDE/VIM),讓我們關注於型別與方法,而不用分心於程式語言的結構。
- 盡量使用鍵盤加速(short-cut),避免使用滑鼠和眼睛找東西,人容易放空。
- 善用實例化需求,用來確認理解是否一致;避免在腦袋中process。遇到對方無法給實例的時候,故意給錯誤的例子,可以引導對方給出例子。
- 完成百分比 ? 不要偏重於完成百分比,而是要注重價值(value),如果只在乎百分比,大家會挑簡單的先做,而不先做最有價值的。(這是為什麼 product backlog items 是單一的 queue)。Project Mgr. 思考的是完成百分比,用來結案驗收收錢,但做 Product 思考點在於價值。
- 測試好不好寫,反映出程式design的好不好 : 很難寫測試程式,代表原來的程式碼設計不佳。
- 不必要的參數,inline掉。
- Try-Catch 要勇敢地拔掉,責任我願意扛,因為要讓產品更好,但要做監控,避免跟其他feature一起上。
- 當其他人停留於抱怨,自己可以在沒有意義中找到金礦,在 Legacy code 上重構,創造自己的價值。(抱怨歸抱怨,還是要想辦法改善)。
# Day 3
1. conway law (康威定律)
2. 分庫分表
3. DAU/MAU
4. primitive obseccion
5. simple design
:::success
### :100: **Simple Design:**
1. 通過測試:沒有滿足需求,就是廢物。用測試驗證符合預期。
2. 呈現意圖 (讀者能用最少的時間、腦力理解程式,且不會誤解。跟可讀性不一樣,兩者是不同概念)。 (許久不說"程式碼可讀性"了,英文報紙哪一個字母你不懂)。表達力與對方的理解要等意。不是要寫得很少,但看不懂,這不叫做simple。
3. 消除重複 (重複是萬惡根源)(重複:代表 有抽象沒有封裝好)(可維護性低,容易漏改,會有bug)。(老師認為,呈現意圖先於消除重複,因為,看懂了才容易消除重複,另,需求有異動時,重複才會造成問題)。
4. the fewest elements 最少元素(越少參數, 越少class, 回傳越簡單越好... etc)若滿足上述三條,最後這一條較簡單。The Fewest Elements : 可以滿足(1)(2)(3)的前提下,讓看的人,用最少的腦力、最少的時間、看得懂你的意圖、不用猜,且搭配測試可以跑。這也是老師對表達力的看法。
-- 參數越少越好 : 同樣完成一件事情 傳入最少的參數
-- eg. 不應該要知道要呼叫兩次 packageService
-- eg. 有 Domain Model 很多事情都不用回傳 (用 void)
物件導向的世界,本身會有很多 void 的方法;只是叫我做事情,還不需要跟我要結果,最後你可以跟我要結果,中間過程keep 在我身上就可以。
-- eg. 越少的class/interface/method/code size
-- eg. 互動次數越少越好
-- eg. 回傳越簡單越好 (沒有回傳 最簡單)
- design pattern 是從實現這些原則而運用出來的,而不是一開始就亂套 design pattern。
- 書 : Refactoring to Patterns。如何用重構的角度,找出哪些彈性或問題需要解決,所以,要運用對應的哪個 design pattern。
- 老師覺得自己的信仰終極會是 Simple Design。
- SOLID/design pattern/micro service… etc.,學的大部分是搞定解耦合。但要做到 "簡單",高內聚才是重點。
- 把 Legacy code 弄得很簡單,比自己重寫更有價值。(安全、穩定的開發)。請忍住只想重寫不想重構。公司裡賺錢的大都是 Legacy code。
:::
### 參考資料
[物件導向程式設計基本原則 - SOLID](https://skyyen999.gitbooks.io/-study-design-pattern-in-java/content/oodPrinciple.html)
[什么是 N+1 问题,以及如何解决](https://segmentfault.com/a/1190000039421843)
[素質培養: VALVE 新員工手冊](https://steamcdn-a.akamaihd.net/apps/valve/hbook-SCH.pdf)
[SimpleDateFormat安全的时间格式化](https://www.cnblogs.com/duanxz/p/3623610.html)
**Refactoring to patterns**
