這讓我瘋了。假設我有以下功能:如何重寫此選擇語句以保證100%的測試覆蓋率?
func Map(quit <-chan struct{}, dst chan<- interface{}, src <-chan interface{}, f func(interface{}) interface{} {
for {
select {
case v, ok := <- src:
if !ok {
return
}
select {
case dst <- f(v):
case <-quit:
return
}
case <-quit:
return
}
}
}
它發送f起SRC接收(V)上的DST爲每個值v直到src或退出關閉和空的或接收到的值從退出。
現在,假設我要編寫一個測試,這表明它可以被取消:
func TestMapCancel(t *testing.T) {
var wg sync.WaitGroup
quit := make(chan struct{})
success := make(chan struct{})
wg.Add(3)
src := // channel providing arbitrary values until quit is closed
dst := make(chan interface{})
// mapper
go func() {
defer wg.Done()
defer close(dst)
Map(quit, dst, src, double)
}()
// provide a sink to consume values from dst until quit is closed
timeout(quit, 10*time.Millisecond)
wait(success, &wg)
select {
case <-success:
case <-time.After(100 * time.Millisecond):
t.Error("cancellation timed out")
}
}
這裏的未定義功能不是非常重要。請假設他們工作。 timeout
在指定的時間後關閉其通道參數,wait
在wg.Wait()
後關閉其通道參數。
問題是,這將不會提供100%的覆蓋範圍,因爲如果兩者都準備好發送/接收,則在(僞)隨機選擇統一的選擇案例。的Map
以下版本沒有這個問題,但是從潛在不定阻擋遭受如果上游信道(SRC)沒有閉合:
func Map(quit <-chan struct{}, dst chan<- interface{}, src <-chan interface{}, f func(interface{}) interface{}) {
for v := range src {
select {
case dst <- f(v):
case <-quit:
return
}
}
}
我可以排序的解決這個通過重寫測試重複幾次循環,以便每個分支都有機會隨機選擇。我已經嘗試了10次迭代,並且所有測試都通過了100%覆蓋率(除此之外還有其他測試)。但它讓我大吃一驚,我似乎無法寫出一個最好的兩全其美的版本,如果上游渠道沒有關閉,並且保證給出100%的測試覆蓋率(不只是可能)。
對我有什麼啓發?
P.S.如果您好奇,爲什麼「不阻止上游通道未關閉」很重要,這只是另一點OCD。這個函數被導出,這意味着如果客戶端代碼行爲不當,我的代碼會出錯。我希望它比第一個版本更有彈性。
我會將它分成多個測試函數。此外,還有兩個你沒有測試的退出情況:從src'接收,然後發送'dst' _then_關閉'quit',並退出,因爲src'已關閉。 https://play.golang.org/p/KI9OJLsHdc – Kaedys
這些已經在其他測試功能中處理過了。只是這個特定的人給了我適合,因爲我沒有意識到我可以通過將通道設置爲零來確定性地遍歷select語句,所以他們永遠不會收到。 – burfl
說實話,甚至不需要設置它們爲零。簡單地讓他們沒有緩衝的頻道,你不叫接收在機械上是相同的渠道爲零。如果您創建了一個無緩衝的'dst'頻道並將它交給'Map',並且從來不打算做一個'<-dst',那麼您的測試根本不會改變。 – Kaedys