constsighs: { Danger: RedLight; Warning: TrafficLight; Safe: GreenLight; } = { Danger: "red", // if you use other words would get error Warning: "yellow", Safe: "there is no constraint any string", };
constsighs: { Danger: RedLight; Warning: TrafficLight; Safe: GreenLight; } = { Danger: TrafficLight.red, // if you use yellow or green would get error Warning: TrafficLight.yellow, Safe: TrafficLight.red, };
What people see here is a weakness in TDD but it is actually a strength, more often than not poor test code is a result of bad design or put more nicely, well-designed code is easy to test.
- The thing you are testing is having to do too many things (because it has too many dependencies to mock) - Break the module apart so it does less - Its dependencies are too fine-grained - Think about how you can consolidate some of these dependencies into one meaningful module - Your test is too concerned with implementation details - Favour testing expected behaviour rather than the implementation
傳說中 KentBeck 大叔說的過的話
Make It Work Make It Right Make It Fast
在本書中 work 意謂通過測試,right 是指重構代碼使意圖明顯好懂,最後 fast 才是優化效能。 如果沒有 work 與 right 之前是無法變 fast 的
章節 reflection 的重構步驟
參考章節 reflection,當完成 slice 的 test case 時候, 程式已經變得相當噁心,
funcwalk(x interface{}, fn func(input string)) { val := getValue(x) switch val.Kind() { case reflect.Slice: for i := 0; i < val.Len(); i++ { walk(val.Index(i).Interface(), fn) } default: for i := 0; i < val.NumField(); i++ {
field := val.Field(i) switch field.Kind() { case reflect.String: fn(field.String()) case reflect.Struct: walk(field.Interface(), fn) } } } }
funcwalk(x interface{}, fn func(input string)) { val := getValue(x) switch val.Kind() { case reflect.Slice: for i := 0; i < val.Len(); i++ { walk(val.Index(i).Interface(), fn) } case reflect.String: fn(val.String()) default: for i := 0; i < val.NumField(); i++ {
field := val.Field(i) switch field.Kind() { case reflect.String: walk(field.Interface(), fn) case reflect.Struct: walk(field.Interface(), fn) } } } }
巢狀 switch 消除重複
在本篇我只對內層的 switch 進行重構, 下面我只展示內層的 switch。 兩個問題,
case 重複(這是我們刻意製造出來的)→ 所以要消重複
消除重複後,參數 field 就變得有點多餘,我們可以用 inline 的手法消除
1 2 3 4 5 6 7
field := val.Field(i) switch field.Kind() { case reflect.String: walk(field.Interface(), fn) case reflect.Struct: walk(field.Interface(), fn) }
測試通過後,我們的內層 switch 就會變成這樣
1 2 3 4
switch val.Field(i).Kind() { case reflect.String, reflect.Struct: walk(val.Field(i).Interface(), fn) }
巢狀 switch default case
這個時候我們看整體的程式碼,會發現一個怪異的現象, 外層的 default 值會直接視作存在多個 Field 進行遞迴拆解 for i := 0; i < val.NumField(); i++ { 內層的 switch 語法只有一個 case 同時處理 reflect.Struct 與 reflect.String 兩種條件, 以邏輯來說,外層的 default 只會處理 reflect.Struct 其它的資料型態都不處理, 而 reflect.String 在同層的 switch 其它條件被處理掉了 所以我們可以把 default 的區段改寫如下,執行測試,通過
1 2 3 4 5 6 7 8
case reflect.Struct: for i := 0; i < val.NumField(); i++ { switch val.Field(i).Kind() { //case reflect.String, reflect.Struct://這個寫法也可以 default://這個寫法比較有交換率的等價概念 walk(val.Field(i).Interface(), fn) } }
funcwalk(x interface{}, fn func(input string)) { val := getValue(x) switch val.Kind() { case reflect.String: fn(val.String()) case reflect.Slice: for i := 0; i < val.Len(); i++ { walk(val.Index(i).Interface(), fn) } case reflect.Struct: for i := 0; i < val.NumField(); i++ { walk(val.Field(i).Interface(), fn) } } }
system.io.ioexception: error loading native library "/app/runtimes/linux-x64/native/libgrpc_csharp_ext.x64.so". error loading shared library ld-linux-x86-64.so.2: no such file or directory (needed by /app/runtimes/linux-x64/native/libgrpc_csharp_ext.x64.so)