关于go:正确的方法来测试使用时间的代码。


Correct way to test code that uses time.Ticker?

我想就如何测试使用time.Ticker

的代码的正确方法提出您的建议。

例如,假设我有一个如下所示的倒数计时器(这只是我想到的一个示例):

1
2
3
4
5
6
7
8
9
10
11
type TickFunc func(d time.Duration)

func Countdown(duration time.Duration, interval time.Duration, tickCallback TickFunc) {
    ticker := time.NewTicker(interval)
    for remaining := duration; remaining >= 0; remaining -= interval {
        tickCallback(remaining)
        <-ticker.C
    }

    ticker.Stop()
 }

http://play.golang.org/p/WJisY52a5L

如果我想测试一下,我想提供一个模拟,这样我就可以快速且可预测地运行测试,因此我需要找到一种方法来使模拟进入Countdown函数。

我可以想到一些实现此目的的方法:

创建一个Ticker接口和包内部的一流函数,我可以对其进行补丁以进行测试:http://play.golang.org/p/oSGY75vl0U

创建一个Ticker接口并将实现直接传递给Countdown函数:
http://play.golang.org/p/i67Ko5t4qk

如果我采用后一种方式,我是否透露了太多有关倒计时工作方式的信息,使潜在客户更难使用此代码?他们不必给出持续时间和间隔,而必须构造并传递股票代号。

我很想知道在测试这样的代码时最好的方法是什么?还是您将如何更改代码以保留行为,但使其更具可测试性?

感谢您的帮助!


因为这是一个非常简单的函数,所以我假设您只是以此为例来模拟非平凡的东西。如果您确实想测试此代码,而不是模拟行情自动收录器,为什么不只使用很小的间隔。

恕我直言,第二个选项是两者中较好的一个,它可以进行用户调用:

1
foo(dur, NewTicker(interval)...

似乎负担不大。

在Go中也有回调,这是严重的代码味道:

1
2
3
4
5
6
7
8
9
10
11
12
func Countdown(ticker Ticker, duration time.Duration) chan time.Duration {
    remainingCh := make(chan time.Duration, 1)
    go func(ticker Ticker, dur time.Duration, remainingCh chan time.Duration) {
        for remaining := duration; remaining >= 0; remaining -= ticker.Duration() {
            remainingCh <- remaining
            ticker.Tick()
        }
        ticker.Stop()
        close(remainingCh)
    }(ticker, duration, remainingCh)
    return remainingCh
}

然后您可以使用以下代码:

1
2
3
4
5
func main() {
    for d := range Countdown(NewTicker(time.Second), time.Minute) {
        log.Printf("%v to go", d)
    }
}

这是在操场上:http://play.golang.org/p/US0psGOvvt


这不能回答如何注入模拟部分,但似乎您正在努力尝试。
如果该示例代表您实际正在测试的内容,则只需使用较小的数字即可。

http://play.golang.org/p/b_1kqyIu-u

1
2
3
Countdown(5, 1, func(d time.Duration) {
        log.Printf("%v to go", d)
    })

现在,如果您正在测试调用Countdown的代码(而不是测试Countdown),那么我可能只是创建一个标记,您可以为您的模块设置一个标志,以相同的调用次数尽可能快地扩展数字。

http://play.golang.org/p/KqCGnaR3vc

1
2
3
4
if testMode {
    duration = duration/interval
    interval = 1
}