Reputation: 1283
I'm confused about the golang unit test.
I have 2 Test_xxx
funcs , like Test_1
and Test_2
.
In Test_1
, i will change a global variable , can Test_2
see the change?
Furthermore, if i use monkey patch
instead of changing global var, will the other Test_xxx
func perceive the patching?
i.e. do i have the necessary to cancel the func substitution using defer when Test_xxx
returns?
Upvotes: 2
Views: 2062
Reputation: 6008
In Test_1, i will change a global variable , can Test_2 see the change?
It could be safe only under some specific conditions:
t.Parallel()
in test.Those are just a few examples from the top of my head. Breaking any of these conditions will break a test. That is why such tests called fragile.
Check if that could be avoided.
Often that requires code change and introducing new patterns like Dependency Injection. Making code testable
is a good thing. You make it more modular and easier to maintain.
Upvotes: 1
Reputation: 99
Note that a future Go version may change the order in which the tests are run, e.g. by randomizing order. If your test depends on the fact that Test_1
runs before Test_2
and changes a global variable, it will break.
A good idiom for changing a global variable is something like:
func Test_1(t *testing.T) {
oldVal := myGlobalVariable
defer func() { myGlobalVariable = oldVal }
// rest of the test
}
Upvotes: 1
Reputation: 1068
While this is possible, you might want to consider initializing the global from both tests to generate consistent behavior. When you run the test from the go
command line you can choose to only run one test function (go test foo -test.run Test_1
) and this will otherwise generate inconsistent results.
Accessing global variables is subject to all sorts of races (in some cases involving partial reads of the data as it is being overwritten elsewhere) returning nonsense/impossible values! Use some sort of sync.Mutex
to protect from these races:
import (
"sync"
)
var mu sync.Mutex
var global int
func readGlobal() int {
mu.Lock()
defer mu.Unock()
return global
}
func writeGlobal(val int) {
mu.Lock()
defer mu.Unock()
global = val
}
// your test functions
Upvotes: 1
Reputation: 66284
Is
Test_xxx
func safe to access shared data in golang?
The answer entirely depends on whether those test functions are allowed to run in parallel.
By default, go test
calls the test functions for a given package sequentially. However, if
t.Parallel()
within both test functions, andyou'll likely get a data race.
To fix ideas, consider this simple test file:
package main
import (
"fmt"
"testing"
)
var count = 0
func Test_1(t *testing.T) {
t.Parallel()
count++
fmt.Println(count)
}
func Test_2(t *testing.T) {
t.Parallel()
count++
fmt.Println(count)
}
If you run go test -race
, the race detector will slap you on the wrist:
==================
WARNING: DATA RACE
--snip--
FAIL
exit status 1
FAIL whatever 0.730s
This should convince you that you should be careful about handling global state in tests. The best thing to do is to avoid global state altogether, if you can. Alternatively, remember that care must be taken to synchronize access to global state as soon as you activate parallel test execution.
Upvotes: 6
Reputation: 4471
In Test_1, i will change a global variable , can Test_2 see the change?
Yes.
var global = 0
func Test_1(t *testing.T) {
for i := 0; i < 1000; i++ {
global++
}
fmt.Println(global)
}
func Test_2(t *testing.T) {
for i := 0; i < 1000; i++ {
global++
}
fmt.Println(global)
}
Out
=== RUN Test_1
1000
--- PASS: Test_1 (0.00s)
=== RUN Test_22
2000
--- PASS: Test_22 (0.00s)
PASS
do i have the necessary to cacel the func substition using defer when Test_xxx returns?
You can use Cleanup function to remove changes of global variable
func Test_1(t *testing.T) {
t.Cleanup(func() {
global = 0
})
for i := 0; i < 1000; i++ {
global++
}
fmt.Println(global)
}
Upvotes: 2