DEV Community

Benjamin DeCoste
Benjamin DeCoste

Posted on

Testing time with Golang

We can all agree that TDD is the bees knees, right? Something that can trip newbies up with golang is writing tests against a function that has a timeout.

Often when waiting for a result over a channel, it can make sense to add a timeout so we don't wait forever. Consider the following code

func doSomething(done chan struct{}) {
  // simulate an operation that takes too long
  time.Sleep(time.Second * 5)
  done <- struct{}
}

// We need to test this
func MyCode() {
  c := make(chan struct{})

  select {
  case <- done:
    // hurray!
  case <- time.After(time.Second * 5)
    // oh no!
}
Enter fullscreen mode Exit fullscreen mode

Writing a unit test that exercises our timeout logic and doesn't take 5 seconds to run can seem difficult at first, but it is actually quite simple. We can use something called remote function overriding to declare a variable that we can override in our test.

var after = time.After
Enter fullscreen mode Exit fullscreen mode

And our switch statement changes like so

select {
  case <- done:
    // hurray!
  case <- after(time.Second * 5)
    // oh no!
}
Enter fullscreen mode Exit fullscreen mode

Now, in our tests, we can easily change the definition of after, to make it happen instantly

func TestSomething(t *testing.T) {
  after = func(d time.Duration) <-chan time.Time {
    return time.After(0)
  }

  // your test code here
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)