--- 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)