Home
/
Time Testing in Go

Time Testing in Go

Testing a background job that needs to happen at a specific time repeatedly is not an easy task. It’s different from the usual deterministic approach where you input something into a function and test the expected result.

Imagine you have a function in Go that needs to run a job every Tuesday at 9 o’clock in the morning. This job could be an import, an export, data fetching, or something else.

Testing is crucial in this functionality. Without proper testing, you would have to wait for weeks to ensure it works correctly. Let’s consider a function that calculates how far we are from the next Tuesday at 9 AM.

package jobs

import "time"

var now = time.Now()

func CalculateNextTuesdayAtNine() time.Duration {
    location := now.Location()
    daysUntilTuesday := int(time.Tuesday - now.Weekday())

    if daysUntilTuesday < 0 || (daysUntilTuesday == 0 && now.Hour() >= 9) {
        daysUntilTuesday += 7
    }

    nextTuesdayAtNine := time.Date(now.Year(), now.Month(), now.Day()+daysUntilTuesday, 9, 0, 0, 0, location)

    return nextTuesdayAtNine.Sub(now)
}

The key to testing our CalculateNextTuesdayAtNine function is how we handle the current time, typically represented by time.Now(). In our initial implementation, we use a global variable now assigned to time.Now(). However, for testing purposes, we need to refactor this approach. By abstracting how we get the current time, we can simulate different times in our tests, making it possible to thoroughly test our function under various scenarios.

Additionally, let’s see how we can use this function in a main application:

package main

import (
	"fmt"
	"time"
	jobs "time-testing/jobs"
)

func main() {
  for {
	duration := jobs.CalculateNextTuesdayAtNine()

	timer := time.NewTimer(duration)
	<-timer.C

	fmt.Println("Running task")
  }
}

Testing the Functionality

Testing our function involves ensuring it accurately calculates the time until the next Tuesday at 9 AM for different dates. Here’s how the test cases are structured:

package jobs

import (
	"fmt"
	"testing"
	"time"
)

func TestCalculateNextTuesdayAtNine(t *testing.T) {

	testCase := []struct {
		name     string
		input    time.Time
		expected time.Duration
	}{
		{
			name:     "First week of December",
			input:    time.Date(2023, time.December, 5, 8, 0, 0, 0, time.UTC),
			expected: 1 * time.Hour,
		},
		{
			name:     "Christmas Day",
			input:    time.Date(2023, time.December, 25, 8, 0, 0, 0, time.UTC),
			expected: 24*time.Hour + 1*time.Hour,
		},
	}

	for _, tc := range testCase {
		t.Run(tc.name, func(t *testing.T) {
			now = tc.input

			result := CalculateNextTuesdayAtNine()

			fmt.Println(result)

			if result != tc.expected {
				t.Errorf("CalculateNextTuesdayAtNine() for %s = %v, want %v", tc.name, result, tc.expected)
			}
		})
	}

}



Source code: time-testing