Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/cabify/timex

A test-friendly replacement for golang's time package [managed by soy-programador]
https://github.com/cabify/timex

Last synced: about 2 months ago
JSON representation

A test-friendly replacement for golang's time package [managed by soy-programador]

Awesome Lists containing this project

README

        

# `timex`

[![Build Status](https://travis-ci.org/cabify/timex.svg?branch=master)](https://travis-ci.org/cabify/timex)
[![Coverage Status](https://coveralls.io/repos/github/cabify/timex/badge.svg?branch=master)](https://coveralls.io/github/cabify/timex?branch=master)
[![GoDoc](https://godoc.org/github.com/cabify/timex?status.svg)](https://godoc.org/github.com/cabify/timex)

`timex` is a test-friendly replacement for the `time` package.

## Usage

Just replace your `time.Now()` by a `timex.Now()` call, etc.

## Mocking

Use `timex.Override(...)` to replace the current implementation by another one, and use the function it returns to restore the default implementation. You can't override from several tests at the same time. You can use an auto-generated by [`mockery`][mockery] mock from `timexmock` package, or a controlled implementation from `timextest`.

### `timexmock`

There's a `timexmock.Mocked(func(mocked *timexmock.Implementation) { ... })` wrapper that automatically creates a mock, sets it as the implementation to be used and defers a tear down to set the default implementation again.

Example:

```go
func TestSleep(t *testing.T) {
timexmock.Mocked(func(mocked *timexmock.Implementation) {
mocked.On("Sleep", someDuration).Once()
defer mocked.AssertExpectations(t)

timex.Sleep(someDuration)
})
}
```

### `timextest`

Timextest provides a more complex API useful to control the behavior of concurrent programs, it is especially useful when the code interacts with timers like `time.Ticker`. Just like `timexmock`, `timextest` also provides a `timextest.Mocked(time.Time, func(*TestImplementation))` function to make mocking easier. Few examples can be found in [`timextest/example_test.go`](./timextest/example_test.go), this is one of them:

```go
func ExampleTestImplementation_NewTicker() {
timextest.Mocked(now, func(mockedtimex *timextest.TestImplementation) {
go func() {
ticker := timex.NewTicker(time.Hour)
for t := range ticker.C() {
fmt.Printf("%s\n", t)
}
}()

tickerCall := <-mockedtimex.NewTickerCalls
tickerCall.Mock.Tick(now.Add(time.Second))
tickerCall.Mock.Tick(now.Add(2 * time.Second))

// Output:
// 2009-11-10 23:00:01 +0000 UTC
// 2009-11-10 23:00:02 +0000 UTC
})
}
```

## Drawbacks

### Performance

There's an obvious performance impact caused by the indirection of the call, it's actually 20-30% slower, however, in absolute numbers we're talking about 30 nanoseconds per call, so you probably should not worry about that. Notice that the difference is so small that it's not easy to get a stable result.

```
$ go test -run=NONE -benchmem -benchtime=5s -bench=. .
goos: darwin
goarch: amd64
pkg: github.com/cabify/timex
BenchmarkTimeNow-4 49619665 112 ns/op 0 B/op 0 allocs/op
BenchmarkTimexNow-4 41256012 145 ns/op 0 B/op 0 allocs/op
```

If you're really worried about performance, you can disable part of the indirection by compiling with `timex_disable` tag, which will provide results similiar to the native implemenation calls:

```
$ go test -run=NONE -benchmem -benchtime=5s -bench=. -tags=timex_disable .
goos: darwin
goarch: amd64
pkg: github.com/cabify/timex
BenchmarkTimeNow-4 49866967 116 ns/op 0 B/op 0 allocs/op
BenchmarkTimexNow-4 47965780 109 ns/op 0 B/op 0 allocs/op
```

### Dogma

Oh... yes, we're changing global variables and we'll obviously burn in hell, but if you're really into DI, you can also accept `timex.Implementation` interface as a dependency, and then inject either `timex.Default{}` or a testable implementation.

[mockery]: https://github.com/vektra/mockery