duanlidi1051 2014-02-22 14:03
浏览 106
已采纳

测试使用time.Ticker的代码的正确方法?

I'd like your advice on the correct way to test code that uses time.Ticker

For instance, let's say I have a countdown timer like below (just an example I thought up for the purposes of this question):

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

If I wanted to test this, I'd want to provide a mock so that I can have tests that run quickly and predictably, so I'd need to find a way to get my mock into the Countdown function.

I can think of a few ways to do this:

Create a Ticker interface and a first class function internal to the package that I can patch for the purposes of testing: http://play.golang.org/p/oSGY75vl0U

Create a Ticker interface and pass an implementation directly to the Countdown function: http://play.golang.org/p/i67Ko5t4qk

If I do it the latter way, am I revealing too much information about how Countdown works and making it more difficult for potential clients to use this code? Instead of giving a duration and interval, they have to construct and pass in a Ticker.

I'm very interested in hearing what's the best approach when testing code like this? Or how you would change the code to preserve the behaviour, but make it more testable?

Thanks for your help!

  • 写回答

2条回答 默认 最新

  • doucao1888 2014-02-22 16:07
    关注

    Since this is a pretty simple function, I assume you are just using this as an example of how to mock non-trivial stuff. If you actually wanted to test this code, rather than mocking up ticker, why not just use really small intervals.

    IMHO the 2nd option is the better of the two, making a user call:

    foo(dur, NewTicker(interval)... 
    

    doesn't seem like much of a burden.

    Also having the callback is serious code smell in Go:

    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
    }
    

    You could then use this code like:

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

    Here it is on the playground: http://play.golang.org/p/US0psGOvvt

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 对于这个复杂问题的解释说明
  • ¥50 三种调度算法报错 采用的你的方案
  • ¥15 关于#python#的问题,请各位专家解答!
  • ¥200 询问:python实现大地主题正反算的程序设计,有偿
  • ¥15 smptlib使用465端口发送邮件失败
  • ¥200 总是报错,能帮助用python实现程序实现高斯正反算吗?有偿
  • ¥15 对于squad数据集的基于bert模型的微调
  • ¥15 为什么我运行这个网络会出现以下报错?CRNN神经网络
  • ¥20 steam下载游戏占用内存
  • ¥15 CST保存项目时失败