2021年3月31日 星期三

[C#] Unit test mock libray - NSubstitute

C# unit test 的 mock library,之前有聽過 moq,似乎是最多人推薦的 C# mock library,不過其實一直沒有用過,最近想要在 unit test裡加 mock 功能,就找了一下資料,發現現在有個新的 mock library NSubstitute 比 moq 還推薦的樣子,就來研究一下。

實際用了之後,發現真的很好用,基本上官網的範例就寫的蠻清楚了,一些常用的功能,設定 method return 值,檢查 method 是否被呼叫,被呼叫的順序,都可以很方便做到,接下來寫 unit test 應該都會用 NSubstitute 來輔助了。

[C#] FolderBrowserDialog 的替代品 Windows API CodePack CommonOpenFileDialog

跳出一個視窗讓 User 選擇某個資料夾,是一個非常常用的功能,.Net framework 裡也有內建了一個元件 FolderBrowserDialog 來提供這個功能,界面就像下面這個樣子。

不過可惜的是,這個內建的元件實在相當的難用,它有幾個問題

  • 第一個是不能指定任意路徑作為根目錄,只有幾個她預設好的路徑可以給你用,例如桌面或是我的電腦。

  • 第二個是沒有提供輸入框讓使用者可以先複製好路徑直接跳過去。

這兩個原因,讓使用者常常需要在樹狀結構的目錄點選好幾次才能選到想要的目錄,非常麻煩,可用性基本為零,更慘的是,.Net framework 一直沒有提供替代的元件,內建的就只有這一個,直到 .Net Core 才將這個元件做了改進。

不過如果不是自己私人的專案,是公司的案子,是沒有那麼容易讓你換平台的,很多公司可能還在用 .Net framework 3.5 呢,怎麼可能為了一個視窗讓你換到 .Net Core 呢?

好在微軟自己出了一個 3rd library:Windows API CodePack,裡面有很多好用的 GUI 元件,其中有一個 CommonOpenFileDialog就解決了這個問題,它的界面像下面這個樣子。

程式碼也相當簡單。

  1. using System;
  2. using Microsoft.WindowsAPICodePack.Dialogs;
  3.  
  4. namespace ConsoleApplication1
  5. {
  6. class Program
  7. {
  8. [STAThread]
  9. static void Main(string[] args)
  10. {
  11. var dialog = new CommonOpenFileDialog()
  12. {
  13. IsFolderPicker = true,
  14. InitialDirectory = @"F:",
  15. };
  16.  
  17. dialog.ShowDialog();
  18. }
  19. }
  20. }

不過不知道為什麼似乎微軟已經把這個 3rd library 拿掉了,也沒有在維護了,所以已經找不到它的官網,不過還是有人把它上傳到 github,也有幫它做更新維護,甚至讓它支援到 .Net 5.0 了,想要使用的人還是可以在網上找到地方下載的。


2021年3月6日 星期六

[Python] 修改 builtin function 行為

遇到一個需求是,需要修改 python print function 的行為,讓每一次 print 呼叫之後都立即 flush。查到 python 有一個 partial function 可以修改 print 的行為。

  1. from functools import partial
  2. print = partial(print, flush=True)
  3.  
  4. import my_module

原本以為只要將程式放到 import 之前,就可以影響到之後 import 的 module,但是發現沒有作用,這是因為 partial 的作用域只有在執行 partial 的該 module 內,無法影響到其它 module。後來發現 python 有個 builtin module,所有內建的函式都在裡面,我們在呼叫內建的函式時,最後都是參考到 builtin module,就想是不是可以直接替換掉 builtin module 內的 print 函式,一試果然就成功了。

  1. import builtins
  2. from functools import partial
  3. builtins.print = partial(print, flush=True)
  4.  
  5. import my_module

替換掉 builtin 內的 print 後,之後所有呼叫的 print,不管是在哪個 module,都會參考到新函式,真是很方便。