Go 如何有效地测试管道和过滤器模式
我正在使用本文中描述的管道和过滤器模式 我想知道如何有效地测试这一点。我的想法是单独测试每个过滤器。例如,我有一个过滤器,看起来像这样Go 如何有效地测试管道和过滤器模式,go,pipeline,goroutine,Go,Pipeline,Goroutine,我正在使用本文中描述的管道和过滤器模式 我想知道如何有效地测试这一点。我的想法是单独测试每个过滤器。例如,我有一个过滤器,看起来像这样 func watchTemperature(ctx context.Context, inStream <-chan int) { maxTemp = 90 go func() { for { select { case <-ctx.Done():
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监视温度(ctx-context.context,inStream我认为你应该稍微改变一下你的结构。首先,测试一个函数是否打印一些东西对我来说一点都不好。日志不应该是你业务逻辑的一部分。它们只是使调试和跟踪更容易的附加组件。其次,你正在启动一个不提供任何输出的goroutine(除了日志)因此您无法控制它何时完成其工作
另一种解决方案:
声明一个通道以从函数中获取输出,最好将其传递给函数
var outStream = make(chan string)
watchTemperature(ctx, inStream, outStream)
不使用普通的日志功能,而是登录到此通道,对于每个输入令牌,您应该创建一个输出令牌:
if temp > maxTemp {
outStream <- "Temperature too high!"
} else {
outStream <- "Normal"
}
if-temp>maxTemp{
outStream工作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!")
}
主程序包
进口(
“字节”
“上下文”
“fmt”
“日志”
“字符串”
)
常数(
最大温度=90
errMsg=“温度过高!”
)
func手表温度(ctx context.context,inStream这对我很有帮助,因为它让我重新思考我的方法。然而,就管道模式而言,goroutine在报告结果后不会停止/结束。因此,除非我报告某个步骤已经完成,否则很难以这种方式进行同步,这与结果通道没有什么不同,对吗?你可以se通道用于发送进度/结果/完成。或者,当完成相同类型/步骤的所有工作人员时,您可以使用sync.WaitGroup
获得通知。是否有一个可选通道作为启动goroutine的函数的参数,如果存在,则将进度更新写入其中,如果不存在,则不使用提供?对于结果/进度通道,最佳做法是在启动worker goroutines的同一函数中创建、返回和延迟关闭它。我认为这将是最后一个问题,其他所有问题我都得到了答案:-)GOODUTIN是否曾经被测试过,但是通过通道来传递数据并观察哪些是从信道中掉出来的?我想我会考虑这个问题,但是声明一个完整的结果类型,它也包含了结果是否是错误的信息。sed,这样多个其他例程可以利用其结果。
inStream <- maxTemp + 1
reply <- outStream
if reply != "Temperature too high!" {
// test failed
}
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!")
}