我正在使用此blog post中所述的管道和过滤器模式。
我想知道如何有效地测试它。我的想法是独立测试每个过滤器。例如,我有一个看起来像这样的过滤器
func watchTemperature(ctx context.Context, inStream <-chan int) {
maxTemp = 90
go func() {
for {
select {
case <-ctx.Done():
return
case temp := <-inStream:
if temp > maxTemp{
log.Print("Temperature too high!")
}
}
}
}()
}
在我的测试中,我现在只想查看是否已打印日志。 我的测试看起来如下。
func TestWatchTemperature(t *testing.T) {
maxTemp = 90
ctx := context.Background()
inStream := make(chan int)
defer close(inStream)
watchTemperature(ctx, inStream)
var buf bytes.Buffer
log.SetOutput(&buf)
inStream<-maxTemp+1
logMsg := buf.String()
assert.True(t, strings.Contains(logMsg, "Temperature too high!"),
"Expected log message not found")
}
由于此过滤器是我的管道的末尾,我没有可以读取的输出通道来确定此goroutine /过滤器是否已经完成了某些操作。
到目前为止,我在网上找到的唯一一件事是,在我的测试中写入inStream之后等待几秒钟然后检查日志。然而,这似乎是一个非常糟糕的选择,因为它简单地引入了竞争条件并且减缓了测试。
测试这样的东西的最佳方法是什么,或者使用我的滤镜设计测试它是不是很好,我总是需要一个outStream?
答案 0 :(得分:1)
我认为你应该改变一下你的结构。首先,测试一个函数是否打印出一些东西对我来说似乎并不好。日志不应该是业务逻辑的一部分。它们只是附加组件,可以更轻松地进行调试和跟踪。其次,你正在启动一个不提供任何输出的goroutine(日志除外),因此你无法控制何时完成它的工作。
另一种解决方案:
声明一个通道以获取函数的输出,并最好将其传递给您的函数。我使用字符串通道尽可能简单。
var outStream = make(chan string)
watchTemperature(ctx, inStream, outStream)
而不是普通的日志工具,请登录此频道,对于每个输入令牌,您应该创建一个输出令牌:
if temp > maxTemp {
outStream <- "Temperature too high!"
} else {
outStream <- "Normal"
}
在每次发送后的测试中,等待输出:
inStream <- maxTemp + 1
reply <- outStream
if reply != "Temperature too high!" {
// test failed
}
答案 1 :(得分:1)
工作人员goroutine并不总是有结果。但是,如果您想确切知道何时完成,则需要使用其中一个并发原语将其与主goroutine同步。它可以是信令通道或等待组。
以下是一个例子:
package main
import (
"bytes"
"context"
"fmt"
"log"
"strings"
)
const (
maxTemp = 90
errMsg = "Temperature too high!"
)
func watchTemperature(ctx context.Context, inStream <-chan int, finished chan<- bool) {
go func() {
defer func() {
finished <- true
}()
for {
select {
case <-ctx.Done():
return
case temp := <-inStream:
if temp > maxTemp {
log.Print(errMsg)
}
}
}
}()
}
func main() {
// quit signals to stop the work
ctx, quit := context.WithCancel(context.Background())
var buf bytes.Buffer
// Make sure, this is called before launching the goroutine!
log.SetOutput(&buf)
inStream := make(chan int)
finished := make(chan bool)
// pass the callback channel to the goroutine
watchTemperature(ctx, inStream, finished)
// asynchronously to prevent a deadlock
go func() {
inStream <- maxTemp + 1
quit()
}()
// Block until the goroutine returns.
<-finished
if !strings.Contains(buf.String(), errMsg) {
panic("Expected log message not found")
}
fmt.Println("Pass!")
}