【问题标题】:How do I tell my test to wait for a callback in a goroutine?如何告诉我的测试等待 goroutine 中的回调?
【发布时间】:2015-08-03 14:31:07
【问题描述】:

我正在使用 dockerclient https://github.com/samalba/dockerclient,它有一个基于通道的 API 来监听事件 client.MonitorEvents() 和一个方便的回调方法 client.StartMonitorEvents(callbackHandler)

我想测试处理程序是否被调用。当然,dockerclient 在 goroutine 中处理事件。

目前,我的处理程序只是吐出一个日志。如果我在测试中等待,一切都会处理。如果我不这样做,它会在处理任何事情之前退出:

func eventCallback(event *dockerclient.Event, ec chan error, args ...interface{}) {
  log.Printf("Received event: %#v\n", *event)
}

我的测试看起来很简单:

func TestReceiveEvent(t *testing.T) {
   createAndMonitorEvents(server.URL)
   <- eventReady
   eventWriter.Write([]byte(someEvent))
   // test for something here
}

当然,由于 goroutine 的原因,除非我输入 time.Sleep(),否则它不起作用。

我如何告诉我的测试,“在运行测试之前等待另一个例程完成它的工作”,而不是进入一些任意的睡眠?我正在测试我的处理程序是否正确处理了该事件。

备用接口client.MonitorEvents() 返回一个频道,这给了我更大的控制权,但从频道接收会吐出无限的nil 事件。

更新:

根据要求,createAndMonitorEvents 是:

func createAndMonitorEvents(url string) {
  // Init the client
  docker, _ := dockerclient.NewDockerClient(url, nil)

  // Listen to events
  stopchan := make(chan struct{})

  go func() {
    eventErrChan, err := docker.MonitorEvents(nil, stopchan)
    if err != nil {
        return
    }

    for e := range eventErrChan {
        if e.Error != nil {
            return
        }
        eventCallback(&e.Event, nil)
    }
    fmt.Println("monitor in place")
  }()
}

【问题讨论】:

  • 什么是createAndMonitorEvents
  • 只是我的main() 调用的一个例程,用于创建 dockerclient 并监控事件。我会更新q。
  • 所以它只是调用处理程序的事件的侦听器。没什么大不了。我的问题是如何让测试等待以确保事件得到正确处理。
  • 大大改变了我的答案——我认为你可以使用MonitorEvents,而你看到的nils 只是频道关闭的症状(你可以通过几种方式处理) .

标签: go goroutine


【解决方案1】:

我认为当您使用MonitorEvents 获得nils 时,您只是看到事件通道已关闭(source of MonitorEvents 包含close(eventOrErrorChan),支持这一点)。 evt, ok := &lt;-c 让您直接检查是否(ok 关闭时为假),for evt := range c 将在关闭后停止。一般来说,从关闭的通道接收是specified 到“[yield] 在收到任何先前发送的值之后元素类型的零值”

关于等待回调的问题:回调可以关闭通道。 (或发送给它。)然后您的测试可以使用select 等待指定的时间长度:

select {
case <-c:
        /* ...success... */
case <-time.After(5 * time.Second):
        /* timed out */
}

如果您知道某些错误情况会导致处理程序无法完成或无法运行,它可能会在不同的通道上发出这些情况的信号,或者通过向c 发送不同的值。

【讨论】:

  • 啊哈!现在我了解了我所看到的nils。我错过了规范的那部分,感谢您指出。不过,我仍然不明白这是如何回答 q 的。如何告诉我的测试“等待回调处理程序响应”?无论哪种方式,+1 以在频道上捕捉到它。
  • 您可以让回调写入(或关闭)通道,然后在通道上等待。换句话说,您可以在回调接口之上构建类似于MonitorEvents 的东西。 (虽然当时我不是这么想的——当我提出这个建议时,我还没有看过MonitorEvents。)
  • "回调接口上有点像MonitorEvents。"这意味着我可以让我的测试监控原始回调代码以检查它是否被调用?
  • 你可以编写回调函数,让它关闭一个通道,测试代码可以从通道接收到等待它。我不确定您正在进行的回调是您的测试代码的一部分还是正在测试的应用程序的一部分。如果回调存在于应用程序中,您可能需要调整您的应用程序,使其接受一个通道,如果非 nil,调用者可以使用该通道等待回调运行。 (与人们对应用程序进行的许多其他类型的更改以使其可测试性没有什么不同。)
  • 它是应用程序的一部分......这就是另一个答案所说的。我已经做了类似的事情来知道何时执行了不同的代码部分(在我的测试中),以便我可以发送信息。
【解决方案2】:

我认为这会有所帮助

WaitGroup 等待一组 goroutine 完成。主 goroutine 调用 Add 来设置要等待的 goroutine 的数量。然后每个 goroutine 运行并在完成时调用 Done。同时,Wait 可以用来阻塞,直到所有的 goroutine 都完成。

http://golang.org/pkg/sync/#example_WaitGroup

【讨论】:

  • 频道比等待组更适合这里。
  • 一般来说,WaitGroup 是一个很酷的想法,但我无从谈起。这需要知道代码中的哪个例程。
【解决方案3】:

“我如何告诉我的测试,“在运行测试之前等待另一个例程完成它的工作”,而不是进入一些任意的睡眠?”

你要么在一个频道上发送,要么关闭一个。调用者正在接收执行阻塞,直到信号发生。我真的不知道如何将你的代码变成有意义的东西......你不能在函数中分配停止通道,你必须将它传递给函数,以便调用者可以监听它。比如这甚至是如何编译的?

func TestReceiveEvent(t *testing.T) {
   createAndMonitorEvents(server.URL)
   <- eventReady // undeclared variable, and discarding the event you recieved?
   eventWriter.Write([]byte(someEvent)) //and another variable that is not declared
   // test for something here
}

也许和想法会帮助你开始......

func createAndMonitorEvents(url string, done chan bool) {
      //the codes
      close(done)
}


func TestReceiveEvent(t *testing.T) {
  eventReady := make(chan bool)
    createAndMonitorEvents(server.URL, eventReady)
    <- eventReady
    eventWriter.Write([]byte(someEvent)) // dis still don't exist though
    // test for something here

}

【讨论】:

  • 所以你的意思是,基本上,唯一的方法是使用计时器,或者有一个明确表示“我完成了”的函数。是这样吗?幸运的是,我控制了函数的源代码(我正在测试自己的东西),所以这会有所帮助。
猜你喜欢
  • 2010-11-08
  • 2018-02-20
  • 1970-01-01
  • 2021-02-17
  • 2020-07-29
  • 2022-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多