---
# System prepended metadata

title: 單元測試課程共筆

---

---
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**
![](https://hackmd.io/_uploads/H19mKazFj.png)

