Google cloud function Golang unit test with init()

Viewed 366

I am having problems creating a Cloud Function in Golang and creating unit tests for it. Specifically, the environment variables that I want to inject during the test phase are not recognized, since the init() function always runs before the test. This init() function uses the environment variables to create a client. Is there any way around this problem?

Google recommends putting global variables like clients in the init() function, to reuse those objects in future invocations. This reduces startup time (source).

Directory structure

/function
  do_something.go
  do_something_test.go

do_something.go

package main

import (
    "context"
    "log"
    "os"

    "cloud.google.com/go/firestore"
)

var (
    projectID = os.Getenv("GCLOUD_PROJECT")
    client    *firestore.Client
)

func init() {
    ctx := context.Background()
    var err error
    client, err = firestore.NewClient(ctx, projectID)
    if err != nil {
        log.Fatalf("Firestore: %v", err)
    }
}

func Main(w http.ResponseWriter, r *http.Request) {
    // Do something
}

do_something_test.go

package main

import (
    "os"
    "testing"
)

func init() {
    os.Setenv("GCLOUD_PROJECT", "my-test-project") // This does not work because of lexigraphical order, init() in do_something.go before this init().
}

func TestMain(t *testing.T) {
    os.Setenv("GCLOUD_PROJECT", "my-test-project") // This does not work because init() is run before TestMain.
}

Possible solutions

  1. Do not use init() in do_something.go. However, this gives a penalty in cloud function performance, latency is increased due to extended startup time.
  2. Inject variables through a configuration file. However, I think this introduces the same problem.
  3. Set environment variables before running go test. This is feasible, however I find this a suboptimal solution, since I don't want to run extra arguments before doing the actual test.
  4. Always default to the development environment unless an environment variable specifies the code runs in production:
projectID = GetEnv("GCLOUD_PROJECT", "my-test-project")

func GetEnv(key string, defaultVal string) string {
    if value, exists := os.LookupEnv(key); exists {
        return value
    }
    return defaultVal
}
1 Answers

You should be exposing an environment variable at project level stating if the code is being executed in test settings or prod settings. Based on this, your init function will set the values of the variables.

Related