timex

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2020 License: Apache-2.0 Imports: 3 Imported by: 2

README

timex

Build Status Coverage Status GoDoc

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 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:

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, this is one of them:

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.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func After

func After(d time.Duration) <-chan time.Time

After can be used a replacement of time.After()

func Now

func Now() time.Time

Now can be used as a replacement of time.Now()

func Override

func Override(implementation Implementation) func()

Override replaces the global implementation used for timex The returned function should be called to restore the default implementation

func Since

func Since(t time.Time) time.Duration

Since can be used as a replacement of time.Since()

func Sleep

func Sleep(d time.Duration)

Sleep can be used as a replacement of time.Sleep()

func Until

func Until(t time.Time) time.Duration

Until can be used as a replacement of time.Until()

Types

type Default added in v1.1.0

type Default struct{}

Default uses time package functions

func (Default) After added in v1.1.0

func (Default) After(d time.Duration) <-chan time.Time

After can be used a replacement of time.After()

func (Default) AfterFunc added in v1.1.0

func (Default) AfterFunc(d time.Duration, f func()) Timer

AfterFunc can be used as a replacement of time.AfterFunc()

func (Default) NewTicker added in v1.1.0

func (Default) NewTicker(d time.Duration) Ticker

NewTicker creates a new ticker as replacement of time.NewTicker()

func (Default) NewTimer added in v1.1.0

func (Default) NewTimer(d time.Duration) Timer

NewTimer creates a new timer as replacement of time.NewTimer()

func (Default) Now added in v1.1.0

func (Default) Now() time.Time

Now can be used as a replacement of time.Now()

func (Default) Since added in v1.1.0

func (Default) Since(t time.Time) time.Duration

Since can be used as a replacement of time.Since()

func (Default) Sleep added in v1.1.0

func (Default) Sleep(d time.Duration)

Sleep can be used as a replacement of time.Sleep()

func (Default) Until added in v1.1.0

func (Default) Until(t time.Time) time.Duration

Until can be used as a replacement of time.Until()

type Implementation

type Implementation interface {
	Now() time.Time
	Since(t time.Time) time.Duration
	Until(t time.Time) time.Duration

	Sleep(d time.Duration)

	After(d time.Duration) <-chan time.Time
	AfterFunc(d time.Duration, f func()) Timer

	NewTicker(d time.Duration) Ticker
	NewTimer(d time.Duration) Timer
}

Implementation defines the methods we delegate

type Ticker

type Ticker interface {
	// C returns the channel where each tick will be signaled, like Ticker.C in time package
	C() <-chan time.Time

	// Stop stops the ticker, no more ticks will be received in `C()`
	Stop()
}

Ticker is an interface similar to what time.Ticker provides, but with C as a function returning the channel instead of accessing a plain struct property, so we can mock it. See time.Ticker docs for more details

func NewTicker

func NewTicker(d time.Duration) Ticker

NewTicker creates a new ticker as a replacement of time.NewTicker

type Timer

type Timer interface {
	// C returns the channel where the tick will be signaled, like Timer.C in time package
	C() <-chan time.Time

	// Stop stops the timer, see exact docs on time.Timer for information about channel draining when Stop returns false
	Stop() bool
}

Timer is an interface similar to what time.Timer provides, but with C as a function returning the channel instead of accessing a plain struct property, so we can mock it. See time.Timer docs for more details

func AfterFunc

func AfterFunc(d time.Duration, f func()) Timer

AfterFunc can be used as a replacement of time.AfterFunc()

func NewTimer

func NewTimer(d time.Duration) Timer

NewTimer creates a new timer as a replacement of time.NewTimer

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL