Soheil Hassas Yeganeh的解决方案通常是一个好办法,或者至少是类似的办法.但这是对API的一个更改,它可能会给调用方带来一些开销(虽然不是很大;如果调用方不需要Done
个通道,调用方不需要它).也就是说,在某些情况下,你不想要那种ACK系统.
我强烈推荐针对这类问题的测试包Gomega.它是专为Ginkgo设计的,但可以独立使用.它包括通过Consistently
和Eventually
匹配器提供出色的异步支持.
也就是说,虽然GoMega可以很好地与非BDD测试系统一起工作(并且可以很好地集成到testing
中),但它是一件相当大的事情,可以是一项promise .如果您只需要这一部分,您可以编写这些断言的您自己的版本.不过,我建议遵循GoMega的方法,即轮询,而不仅仅是单一Hibernate (这仍然处于Hibernate 状态;如果不重新设计API,就不可能解决这个问题).
以下是如何在测试中注意事项.您可以创建一个辅助函数,如下所示:
http://play.golang.org/p/qpdEOsWYh0个
const iterations = 10
const interval = time.Millisecond
func Consistently(f func()) {
for i := 0; i < iterations; i++ {
f() // Assuming here that `f()` panics on failure
time.Sleep(interval)
}
}
mock.devices <- []sparkapi.Device{deviceA, deviceFuncs, deviceRefresh}
Consistently(c.Check(mock.actionArgs, check.DeepEquals, mockFunctionCall{}))
显然,您可以调整迭代和间隔来满足您的需要.(GoMega使用1秒超时,每10ms轮询一次.)
任何Consistently
的实现都有不利之处,那就是无论您的超时时间如何,每次测试运行都要吃掉它.但真的没有办法绕过这一点.你必须决定多长时间才能"不发生".在可能的情况下,最好将您的测试转到Eventually
,因为这样可以更快地成功.
Eventually
有点复杂,因为您需要使用recover
来捕捉panic ,直到它成功,但这并不是太糟糕.大概是这样的:
func Eventually(f func()) {
for i := 0; i < iterations; i++ {
if !panics(f) {
return
}
time.Sleep(interval)
}
panic("FAILED")
}
func panics(f func()) (success bool) {
defer func() {
if e := recover(); e != nil {
success = true
}
}()
f()
return
}
最终,这只是您已有内容的一个稍微复杂的版本,但是它将逻辑包装到一个函数中,这样它的可读性会更好一些.