Dependency Injection - Extract Dependency Object
External Dependency 是系統中與被測試程式互動但你無法控制的物件。被測試程式受到 external dependency 行為影響可能有不同結果,為了保持 unit test 穩定,不會一下結果該是 A、一下結果該是 B,我們希望能掌控 external dependency──藉由 stub object 模擬 external dependency 的行為並將其 inject 到被測試程式中,基本步驟如下:
- 抽出 external dependency object 的 interface
- stub implement 該 interface 並實作 function
- inject stub 到被測試程式
- Constructor Injection
- Setter Injection
- Extract and Override
本篇用例子說明步驟 1 跟 2:MyClass 是被測試程式,Foo 是 external dependency。(為了讓 code 短一點直接在 header 實作)
Stub
External Dependency 是系統中與被測試程式互動但你無法掌控的物件。互動就是有 call 啊、使用回傳值之類的。
stub 是在系統中產生一個可以控制的替代 object 來取代 external dependency object。
使用 stub 可以解決直接相依帶來的測試問題:無法控制相依物件的行為及回傳值(例如每次 call third party API 得到的結果不同)或者相依物件不穩定,而難以有穩定的環境(固定的 input 及 output)測試要測試的程式邏輯。
一種典型的 stub 是回傳假資料,藉由假造不同的回傳值來測試程式在不同情境下的運作,例如假造其他 function 的各種可能的回傳值。
Visual Studio Code
Build & Debug C++
安裝 extension C/C++ (ms-vscode.cpptools)。
Build
ctrl + ship + p 輸入 tasks,選擇 Configure Task,選個 template 來改。或者直接在 .vscode/ 下新增 tasks.json:
1 | { |
這是有 Makefile 的設定方式,command 也可以用 g++ 配合 args。
Debug
左邊切到 Debug 按上面的齒輪可以設定 launch.json,也可以直接在 .vscode/ 下新增設定檔。
1 | { |
設定好之後按 F5 就可以 Debug 囉~
breakpoint 只要在 code line number 左邊按出紅點點就可以啦~(g++ 要記得 -g)
Extension
workspace 推薦 extension
在 .vscode/ 底下加入 extensions.json,可以在裡面列推薦跟不推薦的 extension。
1 | { |
Git History
ctrl + shift + p 後打 git h 可以看 git log、file 跟 line history。
選一個 commit 點下面的修改檔案還可以看 diff。
Sublime Text Keymap and Settings Importer
如果習慣 sublime 的 keymap 可以用這個 extension,其他還有 Visual Studio、Eclipse 等等的 keymap。
Troubleshooting
檔案太多 vscode 無法 watch changes
這裡說可以修改 /etc/sysctl.conf,加上:
1 | fs.inotify.max_user_watches=524288 |
然後下 sudo sysctl -p load 進設定。
也可以設定 vscode 的 files.watcherExclude 來 exclude 一些不想 watch changes 的 folder。
Ref
Visual Studio 2017 NUnit Usage
NUnit 是 .NET 的 unit test framework
建立專案
- 新增「Visual Studio C# 類別庫 (.Net Framework)」專案
- 在 solution 裡加入 unit test 新專案,一樣是 Visual Studio C# 類別庫 (.Net Framework)
unit test project 可命名為 [Project].UnitTests。
安裝 NUnit 套件
在 unit test project 右鍵 → 管理 NuGet 套件 → 搜尋 → 安裝。
安裝 NUnit、NUnit3TestAdapter 套件,NUnit 裝完可以在參考看到 nunit.framework。
安裝的 NUnit 版本是 3.11.0。
寫 & 跑測試
在 unit test project 加入要測試的 project 的參考。
在 class 前標註 [TestFixture] 表示 NUnit 測試的類別,在 function 前標註 [Test] 表示測試。
選單→測試→執行→所有測試,就會出現「測試總管」顯示測試結果啦~
The Nature of Software Development
薄薄一本、每頁短短的還有可愛插圖(?),觀念不難但做到很難。
軟體開發環繞著「value」,翻成中文我想是「價值」。
value is “what you want”.
重要概念:
- 決定 value 是什麼或者說要什麼,feature 會為軟體帶來 value
- 以重要性決定做 feature 的順序
- 以 feature 組織 team
- feature by feature 開發,以 feature 了解目前進度
- 頻繁 deliver 得到回饋以知道是否需要調整 feature、方向甚至可以結束開發
每個概念都有更細的觀念跟具體作法,不過本書不太提具體作法。
Agile
整本書概念算是環繞著 Agile 的精神吧,看完最大感想:
Agile is simple, but not easy.
Agile 應該不只是開發人員的事,也包含業務甚至客人,而光開發要能做到就已經很困難了。
我沒有長時間跑 Agile style 開發的經驗,不知道長時間運作的結果,也不知道跑起來順的 Agile 是什麼樣子。只有拿相關東西做些實驗,例如 unit test 跟自動化測試,但連 CI 都還稱不上。
對開發而言光是「隨時保持軟體可正常運作、可以 deploy」,也就是完整 CI/CD 就已經很不容易了。
Amdahl's law
提升系統某部份效能(減少執行時間)時,對系統整體的影響取決於該部份佔系統的比重以及其效能提升的程度。
某個程式原本的執行時間是 T,效能提升後執行時間為 T'。其中某部份程式的執行時間為 t、其佔全部執行時間的比例為 a = t / T。假設此部份提升效能後的執行時間為 t',即該部份效能提升為 k = t / t',則整個程式的效能提升為:
S = T / T' = 1 / ((1 - a) + a / k)
是個蠻直覺的定理。在 Performance of Concurrency 有寫過,這篇是比較 general 的版本。
Clean Code
忙了半年左右,我回來了(?),來寫點早該看完的《Clean Code》小心得。
整本書講 clean code 的原則,最後以一個大例子運用原則跟示範 refactor。寫那些原則像在抄書,只針對比較有感的原則跟 code smell 寫點心得。
最少驚奇(嚇)原則
讓看你的 code 的人(很可能就是你自己)對那段 code 的預期符合實際結果,儘量符合 common sense 以減少驚奇、驚嚇與驚恐。code 愈能符合預期,愈能減少進去實作看細節的時間。反之,如果常常不符預期,不用多久大家就會戒慎恐懼的每次都進去看實作,不然天知道它會發生什麼事,對吧?
不過我承認,似乎不是所有人的 commen sense 都一樣。
win10 與 linux 多重開機的糾葛(?)
原本電腦有用 UEFI 裝的 win7 跟 debian,用 grub2 開機。
多灌 win10
其實從來沒搞懂 UEFI 在幹嘛,只知道比古早時代的 BIOS 新,然後常常造成我灌系統的困擾(喂)。
這次用新硬碟裝 win10,原本用 legacy mode 裝起來,但 update-grub 抓不到,在 /etc/grub.d/40_custom 寫 google 來的各種 menuentry 也不行。改用 UEFI mode 裝 win10,它一下說找不到磁碟分割表(明明看起來就有),一下又說無法設定重開機所以不能裝,但明明我 BIOS 裡的 boot priority 已經選那顆新硬碟了(不過事後懷疑說不定應該要選 win7 的那顆才對)。在 BIOS 裡東調西調,從相容 legacy mode 換成 windows 8/10 又換成 windows 8/10 的另一種模式總算裝起來(裝完還把我 linux 那顆硬碟的 SATA port 關掉不知道是怎樣= =)。
看起來 win10 把自己的開機區裝到原本 win7 的 UEFI 系統,所以不動原本 grub 設定的情況下,從原本開 win7 的 partition 進去,就可以再選 win10 或 win7 開機。只是如果要開 win7,它會再重開一次……= =a…..(後來懷疑說不定是因為 win10 預設不會「真關機」,所以某些情況下開 win7 會需要重開,不過只是猜測,沒試過)
grub2 要加開機選項一般只要 update-grub。如果有自訂需求,到 /etc/grub.d/ 底下手動加再 update-grub。
在 Linux mount 成 read-only 的 NTFS
裝 win10 後的某天,我在 linux 裡要存檔案到原本 windows 下放資料的硬碟(理所當然的是 NTFS)。
嗯?為什麼寫不進去?ro?為什麼變 ro 了?之前都可以 rw 啊。
umount 又 mount 了一陣,ro 就是 ro,錯誤訊息看起來是說 windows 休眠中,所以不給 mount rw 免得壞掉。
蛤???windows 休眠中???可是我現在是開機成 linux 啊???哪來的 windows???感覺就是 win10 的錯(喂)。重開機進 win10,再重開進 linux,嗯?又好了?從 win10 關機,開機進 linux,又 ro。
這篇 給了我答案。簡單來說,win10 的 shut down 不是真的 shut down,reboot 才會真的 shut down(講人話)。好吧,把那個快速啟動關掉,我也不差那幾秒。win10 關機,開機進 linux,耶,世界一片美好。
Exception Handling
前陣子寫 code 遇到 exception 或程式執行錯誤該丟出 exception 的情況,雖然知道 try catch 跟 throw exception,但「什麼時候」要「如何使用」exception 卻沒個概念然後搞得一團亂。只好來念念《例外處理設計的逆襲》,順便整理下嗑完書的簡略筆記。
區分 Fault、Error、Failure、Exception
首先區分幾個名詞的概念,不然這些東西都很像,不分清楚講到後來都不知道在講些什麼了。
- (從外部看)一個 component 沒有提供正確服務稱為 service failure,簡稱 failure。
- error 是種「狀態」,表示 component 內部處於錯誤狀態。這種狀態可能導致 component 執行失敗,造成 failure。
- fault 則是導致 error 發生的「原因」。
- 程式語言以 exception 表達 error 與 failure 的概念。
- 從不同角度來看(如 caller 跟 callee),exception 可以代表 error 也可以代表 failure。
- 遇到某種情況要不要丟出 exception,決定方式之一是看在該 function 的語意及功能下,該狀況是不是屬於「目前可能處在不正確的狀態」或「被 call 的 function 沒做好該做的事」。如果是,當然就丟出 exception。