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!")
}