How do I know I'm running within "go test"
Asked Answered
S

13

63

When I run "go test", I want to use a different configuration file. How do I know within my code if I'm running within a test context or a normal context? Is there some sort of environment variable to check?

Schlep answered 10/1, 2013 at 1:5 Comment(3)
Did you ever figure out a good solution to this? I'm dealing with the exact same problems with almost the exact same config setup as you.Speedball
@Speedball There might be a "good solution", currently being implemented, for Go 1.22 (Q3 2023).Macedoine
testing.Testing() - added in Go v1.21 works fine for me (see pkg.go.dev/testing#Testing)Tufa
M
60

The testing package modifies the global environment when loaded: it registers a lot of command-line flags. So, we can check if these flags are registered:

func init() {
    if flag.Lookup("test.v") == nil {
        fmt.Println("normal run")
    } else {
        fmt.Println("run under go test")
    }
}
Marybethmaryellen answered 16/4, 2016 at 15:33 Comment(5)
It is important to note that if you import testing in any non *_test.go file this approach will not work. It will always assume the test run. I ran into this when I added some utility functions that allowed a testing.T to be passed for failures.Prague
Experimentally, this answer seems correct. It's important to understand that we want to simply test for the presence/absence of the flag (flag.Lookup("test.v") == nil), which indicates that the testing framework has registered the flag. Furthermore, we don't care about the actual value of the flag if it's present (checking from init(), there's a good chance it's not even been parsed yet).Salmonella
This worked fine for me!! ThanksCatercousin
This doesn't work anymore on the more recent versions of Go.Heulandite
@Heulandite I'm running 1.19.4 and it's working as described above.Facultative
U
42

Since the flag.Lookup("test.v") approach does not work for me, I'd like to share my own solution:

strings.HasSuffix(os.Args[0], ".test")

Or

strings.Contains(os.Args[0], "/_test/")

Both seem to work.

Unesco answered 28/8, 2017 at 6:43 Comment(5)
The flag.Lookup("test.v") approach used to work, now it doesn't (I'm using 1.8.1).Martin
strings.HasSuffix(os.Args[0], ".test") is more reliable for me. There is one development machine where strings.Contains(os.Args[0], "/_test/") failed.Parsonage
This worked for me while the flag.Lookup("test.v") did not. In fact, when I looked at the flag.formal map (which is what is indexed by flag.Lookup), there were no flags at all.Kazan
That will not work if your test runner called its binary without a .test extension using the -o flag. FWIW the flag.Lookup("test.v") method appears to work in 1.18.Pardue
Normally the ideal is to use TestMain. However, this approach helped me for preparing/setting-up Example*_* functions in the test. It was useful to simplify the example and focus on showing how to use the functions/methods. Without using extra code like mocking just for the examples.Haber
P
12

The flag.Lookup("test.v") == nil method seems to work but I would recommend using TestMain, which is specifically designed to allow specific setup and teardown for tests. If you include this method in the package you want to test, it will be called before your individual tests and so you can set a global flag or perform whatever test-specific configuration you want to do before calling m.Run() as follows

func TestMain(m *testing.M) {
    // test context initialization here
    os.Exit(m.Run())
}

PS: this is actually what @calvin-sugianto suggested, except Gin has nothing to do with it

Prussianism answered 7/3, 2019 at 8:59 Comment(4)
This appears to be the best answer as it is documented on golang.org and will continue to be supported. The answers based on undocumented flags should be avoided imo.Strawflower
This is a great point, but has the issue of not being called before init(), so cannot be used to control actions of init()Sudderth
@AndrewPrice Would you be so kind as to provide a link to where this is documented on golang.org?Whimsey
@Whimsey here it is: pkg.go.dev/testing#hdr-MainMarcel
H
9

One possibility would be to use build constraints. If you run go test as:

go test -tags testing pkgname

Then you can use that tag to select which files will be included in a standard build of your package, and which will be used for testing.

If you put your standard configuration in a file of its own, then adding a line to the top like the following will ensure that it is not used for testing:

// +build !testing

You can then include the testing configuration in one of the *_test.go files, or in some other file that requires that the testing tag be set.

Hernardo answered 10/1, 2013 at 11:20 Comment(3)
Ideally, there shouldn't be any sort of magic word necessary to indicate that the code is running within a unit-testing context. "go test" should set something that indicates this is unit-testing.Schlep
no it shouldn't. You are right that there shouldn't be a magic word necessary to indicate the is running within a unit-testing context. not from go test and not from you. Your tests should have a different function setting up the test enviroment which your test files then call explicitlyThanatopsis
The problem with having "different function setting" is that it relies on the ernest effort of engineers. If my framework needs to swap out mock testing bits during my framework users' tests, I want to do it automatically under my control as a framework designer. Note that this does not preclude the ability for tests to craft refinements in the behavior of my framework during testing. But it does rely on my ability to known when I'm running in a test environment.Curia
T
8

Code examples would help. But from your question it sounds like you've hardcoded a path to a config file somewhere when you probably wanted to pass it in instead.

Change the function you are testing to take a parameter defining the config file and then in your test code pass a different path in than you use in the non test code. It's bad practice for your code to have a different path when testing vs production.

Thanatopsis answered 10/1, 2013 at 2:33 Comment(3)
I don't have hard-coded paths. I have different configuration files for each environment (dev, beta, prod, etc). I want a "unit-testing" environment that is used whenever "go test" is run. Right now, I'm having to do "ENV=test go test ./..." and that's janky. I was hoping there would be something that I can test that "go test" sets itself.Schlep
Then have your test functions use the dev config file as part of their setup. This should not be determined from code that will run in production. That's asking for all kinds of trouble.Thanatopsis
I agree with Jeremy. The path to configuration should not be detected accorig whether run test or not. That code smells. You should explicitely write a path - one in function main, second in your test function. Do not hide your dependency.Jakoba
P
5

I am new to golang and flag.Lookup("test.v") did not work for me, So i have found way to identify if context is testing or normal by setting ENV variable in init().

If you have test file for example abc.text.go, set env variable "GO_ENV" to testing in init().

func init() {
    os.Setenv("GO_ENV", "testing")
}

And where ever you want to know the context use os.Getenv("GO_ENV")
abc.go

if os.Getenv("GO_ENV") == "testing" {

} else {

}
Pelite answered 22/12, 2019 at 13:58 Comment(0)
C
3

The flag.Lookup("test.v") solution does not work for me either (go 1.13), and I have lots of services to maintain and it is not feasible to use the func TestMain(m *testing.M) solution in every service. As all the services share a common library, I've come up with the following solution:

func isInTests() bool {
    for _, arg := range os.Args {
        if strings.HasPrefix(arg, "-test.v=") {
            return true
        }
    }
    return false
}

This is similar to the flag.Lookup("test.v") solution because we check if -test.v is present.

EDIT: updated the code based on @mh-cbon comment

Cholecystectomy answered 20/11, 2019 at 0:50 Comment(1)
I would do if strings.HasPrefix(arg, "-test.") {. Your code won't catch running test is the -v flag is set to false.Aubine
H
1

Similar to Pram's answer I might set an environment variable when you run the tests.

Makefile

test:
 ENV=testing go test ./...

main.go

  env := os.Getenv("ENV")
Hairstyle answered 9/11, 2021 at 0:44 Comment(0)
T
1

Since Go v1.21, you can use testing.Testing() (docs: https://go.dev/doc/#Testing)

import (
    "fmt"
    "testing"
)

func main() {
    if testing.Testing() {
        fmt.Println("We are testing")
    } else {
        fmt.Println("We are NOT testing")
    }
}

(See it in action - Go Playground: https://go.dev/play/p/F3oMiPoPBHm)

Tufa answered 25/5 at 11:29 Comment(0)
P
0

If your trying to detect if the file is being run through "go run" vs an executable, this function checks if the directory contains "/Temp/go-build" (also checks for \ for windows support)

if strings.Index(os.Args[0], "/Temp/go-build") != -1 || strings.Index(os.Args[0], "\\Temp\\go-build") != -1 {
    // ran with go run
} else {
    // ran as executable file
}
People answered 24/7, 2020 at 16:23 Comment(0)
C
0

A different approach will be using a seperate init() func in test file per package.

Since *_test.go runs only during 'go test' we could

feature/feature_setup_test.go

package feature

fun init() {  
       fmt.Println("Code For Test initialization")
}

This init() func from test is called only during testing; Unlike an init() func from feature.go which will with every import/run.

And one more thing when we do testing, init() in test will get executed only after non-test init() function.

So you can override the a feature.go -> init() with feature_test.go -> init().

Sample code available in gist.

Clary answered 14/7, 2022 at 12:46 Comment(0)
G
-1

More robust solution that Garrett's one is:

 func init() {
    if v := flag.Lookup("test.v"); v == nil || v.Value.String() != "true" {
        fmt.Println("normal run")
    } else {
        fmt.Println("run under go test")
    }
 }
Geometric answered 23/6, 2017 at 8:15 Comment(2)
It's not Garrett's, it's my answer. And we don't need to know value of this flag, all we need to know is package "test" was loaded and thus has registered this flag.Marybethmaryellen
I am seeing this usage in the wild and want to re-iterate the comment by @Marybethmaryellen that it is incorrect. Testing the value for true/false is effectively testing to see that the verbose testing flag was given, which is irrelevant. Furthermore, if used in init(), it's likely that flag.Parse() has not yet been called, so the value cannot be used. It's sufficient to check that flag.Lookup("test.v") != nil to detect the testing framework.Salmonella
R
-1

Check if _test.go exist in stack.

package utils

import (
    "runtime/debug"
    "strings"
)

func IsInTest() bool {
    stacks := strings.Split(string(debug.Stack()), "\n")
    for _, line := range stacks {
        if strings.HasPrefix(line, "\t") {
            path := strings.Split(strings.TrimSpace(line), ":")[0]
            if strings.HasSuffix(path, "_test.go") {
                return true
            }
        }
    }
    return false
}
Rogatory answered 17/7, 2022 at 7:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.