Live data from Hacker News

Mocking time and testing event loops in Go

dmitryfrank.com

31–35 of 35 posts

Re: Mocking time and testing event loops in Go

#31
I've maintained a few codebases that relied directly on stdlib time in ways that were hard to write tests for. Sometimes the code works reliably, but tests are flaky, due to the author using time.Sleep in tests, or something else to paper over hard-to-test functions.

I'd say in a lot of, or most circumstances it's best to just bite the bullet and refactor the mess into something more maintainable.

But there are exceptions to every rule, and let's face it, sometimes it's just too expensive.

Well what you can often do in these circumstances is just mock out the whole damn time package. That's what I did when faced with a similar situation and I was pleasantly surprised with the results.

I wrote https://github.com/echlebek/timeproxy to be a drop-in import for stdlib time. (Modulo any drift in stdlib since it was written)

I wrote https://github.com/echlebek/crock as a fake time implementation that I can exercise complete control over. It lets me control tickers, timers, time.Now, and all that however I like.

Since timeproxy just dispatches all calls to stdlib by default, I feel confident using it in production.

crock has some bugs, but they usually only show up in pathological cases, and you can easily prove that its code path will only get executed in tests, by ensuring that it only gets imported in _test.go files.

So far to my knowledge I'm the only user of these libraries, although I have employed them in a large open source application. Really, the libraries are less important than concept, which I learned from a Go author's slide deck on mocking out the os library years ago.

Re: Mocking time and testing event loops in Go

#32
post #26
post #18

If you're using systemtime for anything else than dumping into logs as timestamps, you're doing it wrong. Mocking system clocks for testing just confirms that truth. If you can't think of an alternative to using some variation of systemtime function for business operational rules, I'm not really sure what you're pushing to production.

Go doesn't differentiate the program's monotonic time from systemtime in terms of the package interface given. time.Now in go returns both the system time (i.e. if you do '.Seconds' or 'String' on that time, you get a wall clock time), but also returns a monotonic time (if you subtract or compare two times, it uses monotonic time). Go's time package isn't just systemtime, and there's really not an alternative to it i…

I don't think you got my point though. If you're using timestamps for anything else than dumping into a logfile (or purely for displaying/cosmetic purposes), you're doing it wrong. Somewhere along the line, you could end up with a nasty surprise. Not thinking of clock drift or any kind of seasonal adjustments here.

Maybe you're using time-package right and avoiding the above, maybe not. Timestamp usage is best to avoid for anything business critical.

Re: Mocking time and testing event loops in Go

#33
post #15

Makes me wonder how simulating time could work? Are there any good reads on how you'd model an environment where variable time intervals are critical factors in the simulation?

An interesting use case I saw was on a complex VoIP system with lots of internal message passing, following an actor model. There were tens of thousands of unit tests to assert all kinds of crazy interop scenarios, and the system clock was mocked out globally.

So you could set up multiple incoming streams, advance the clock to a very high level of precision and assert e.g packet A triggered packet B after 20ms, or timer X fired after 1s. This made the tests extremely reliable and fast to run, since you were pretty much CPU bound - something that I don't think would have been otherwise possible.

Re: Mocking time and testing event loops in Go

#34
post #32
post #26

Earlier quoted context omitted.

Go doesn't differentiate the program's monotonic time from systemtime in terms of the package interface given. time.Now in go returns both the system time (i.e. if you do '.Seconds' or 'String' on that time, you get a wall clock time), but also returns a monotonic time (if you subtract or compare two times, it uses monotonic time). Go's time package isn't just systemtime, and there's really not an alternative to it i…

I don't think you got my point though. If you're using timestamps for anything else than dumping into a logfile (or purely for displaying/cosmetic purposes), you're doing it wrong. Somewhere along the line, you could end up with a nasty surprise. Not thinking of clock drift or any kind of seasonal adjustments here. Maybe you're using time-package right and avoiding the above, maybe not. Timestamp usage is best to avo…

We're misunderstanding each other then.

I assumed this package about mocking time was _not_ about mocking timestamps but program behavior. For example, if I have a loop that says "every 20 seconds, do X", and I want to test that, I now have to mock time.

That's not about timestamps, that's about comparing monotonic clocks for "20s have elapsed".

The go time package doesn't just give you a timestamp to do something with, it also lets you compare times. That was my point.

It sounds like your original comment was arguing that mocking time isn't useful, so I assumed you meant all uses of time.

Re: Mocking time and testing event loops in Go

#35
post #34
post #32

Earlier quoted context omitted.

I don't think you got my point though. If you're using timestamps for anything else than dumping into a logfile (or purely for displaying/cosmetic purposes), you're doing it wrong. Somewhere along the line, you could end up with a nasty surprise. Not thinking of clock drift or any kind of seasonal adjustments here. Maybe you're using time-package right and avoiding the above, maybe not. Timestamp usage is best to avo…

We're misunderstanding each other then. I assumed this package about mocking time was _not_ about mocking timestamps but program behavior. For example, if I have a loop that says "every 20 seconds, do X", and I want to test that, I now have to mock time. That's not about timestamps, that's about comparing monotonic clocks for "20s have elapsed". The go time package doesn't just give you a timestamp to do something wi…

Fair enough. If you want to mock time differentials like that, I see that one could use something to mock that. However, what you probably ideally would want to mock are the order of events, and not certain language-specific stdlib-calls. Though would depend how explicit you want to define your domain.
Post reply on HN