Test_xxx func 在 golang 中访问共享数据是否安全?
Is Test_xxx func safe to access shared data in golang?
我对 golang 单元测试感到困惑。
我有 2 个 Test_xxx
函数,比如 Test_1
和 Test_2
.
在 Test_1
中,我将更改一个 global 变量,Test_2
可以看到更改吗?
此外,如果我使用 monkey patch
而不是更改 global var,其他 Test_xxx
func 会感知补丁吗?
即我是否有必要在 Test_xxx
returns?
时使用 defer 取消函数替换
In Test_1, i will change a global variable , can Test_2 see the change?
是的。
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)
}
出来
=== 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?
可以使用Cleanup函数去除全局变量的变化
func Test_1(t *testing.T) {
t.Cleanup(func() {
global = 0
})
for i := 0; i < 1000; i++ {
global++
}
fmt.Println(global)
}
Is Test_xxx
func safe to access shared data in golang?
答案完全取决于是否允许这些测试函数运行并行。
默认情况下,go test
按顺序调用给定包的测试函数。但是,如果
- 您在两个测试函数中都调用了
t.Parallel()
,并且
- 两个函数都访问(write/write 或 write/read)同一个全局变量,它们之间没有任何同步,
您可能会遇到数据竞争。
要修正想法,请考虑这个简单的测试文件:
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)
}
如果你运行go test -race
,种族检测器会拍打你的手腕:
==================
WARNING: DATA RACE
--snip--
FAIL
exit status 1
FAIL whatever 0.730s
这应该使您相信在测试中处理全局状态时应该小心。如果可以的话,最好的办法是 avoid global state altogether。或者,请记住,一旦激活并行测试执行,就必须注意同步对全局状态的访问。
虽然这是可能的,但您可能需要考虑从两个测试中初始化全局以生成一致的行为。当您从 go
命令行 运行 测试时,您可以选择仅 运行 一个测试函数 (go test foo -test.run Test_1
),否则会产生不一致的结果。
访问全局变量会受到各种竞争的影响(在某些情况下涉及部分读取数据,因为它正在其他地方被覆盖)返回 nonsense/impossible 值!使用某种 sync.Mutex
来防止这些种族:
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
请注意,未来的 Go 版本可能会更改测试的顺序 运行,例如通过随机顺序。如果您的测试依赖于 Test_1
运行s 在 Test_2
之前并更改全局变量这一事实,它将中断。
更改全局变量的一个很好的习惯用法是这样的:
func Test_1(t *testing.T) {
oldVal := myGlobalVariable
defer func() { myGlobalVariable = oldVal }
// rest of the test
}
In Test_1, i will change a global variable , can Test_2 see the change?
只有在某些特定条件下才是安全的:
- 您 运行 在单个 Goroutine 中进行测试。您不能在测试中使用
t.Parallel()
。
- 您只能 运行 测试一次。否则,您必须在每次测试 运行.
后实施额外的拆卸例程以将数据重置为原始状态
- 您不能更改文件中的测试顺序。开发人员过去常常依赖于功能顺序并不重要。对于在不更改代码的情况下移动测试的人来说,对顺序的依赖可能会非常混乱。
这些只是我脑海中的几个例子。打破这些条件中的任何一个都会破坏测试。这就是为什么这样的测试称为脆弱。
检查是否可以避免这种情况。
通常这需要更改代码并引入新模式,例如 Dependency Injection。制作代码 testable
是一件好事。你让它更模块化,更容易维护。
我对 golang 单元测试感到困惑。
我有 2 个 Test_xxx
函数,比如 Test_1
和 Test_2
.
在 Test_1
中,我将更改一个 global 变量,Test_2
可以看到更改吗?
此外,如果我使用 monkey patch
而不是更改 global var,其他 Test_xxx
func 会感知补丁吗?
即我是否有必要在 Test_xxx
returns?
In Test_1, i will change a global variable , can Test_2 see the change?
是的。
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)
}
出来
=== 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?
可以使用Cleanup函数去除全局变量的变化
func Test_1(t *testing.T) {
t.Cleanup(func() {
global = 0
})
for i := 0; i < 1000; i++ {
global++
}
fmt.Println(global)
}
Is
Test_xxx
func safe to access shared data in golang?
答案完全取决于是否允许这些测试函数运行并行。
默认情况下,go test
按顺序调用给定包的测试函数。但是,如果
- 您在两个测试函数中都调用了
t.Parallel()
,并且 - 两个函数都访问(write/write 或 write/read)同一个全局变量,它们之间没有任何同步,
您可能会遇到数据竞争。
要修正想法,请考虑这个简单的测试文件:
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)
}
如果你运行go test -race
,种族检测器会拍打你的手腕:
==================
WARNING: DATA RACE
--snip--
FAIL
exit status 1
FAIL whatever 0.730s
这应该使您相信在测试中处理全局状态时应该小心。如果可以的话,最好的办法是 avoid global state altogether。或者,请记住,一旦激活并行测试执行,就必须注意同步对全局状态的访问。
虽然这是可能的,但您可能需要考虑从两个测试中初始化全局以生成一致的行为。当您从 go
命令行 运行 测试时,您可以选择仅 运行 一个测试函数 (go test foo -test.run Test_1
),否则会产生不一致的结果。
访问全局变量会受到各种竞争的影响(在某些情况下涉及部分读取数据,因为它正在其他地方被覆盖)返回 nonsense/impossible 值!使用某种 sync.Mutex
来防止这些种族:
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
请注意,未来的 Go 版本可能会更改测试的顺序 运行,例如通过随机顺序。如果您的测试依赖于 Test_1
运行s 在 Test_2
之前并更改全局变量这一事实,它将中断。
更改全局变量的一个很好的习惯用法是这样的:
func Test_1(t *testing.T) {
oldVal := myGlobalVariable
defer func() { myGlobalVariable = oldVal }
// rest of the test
}
In Test_1, i will change a global variable , can Test_2 see the change?
只有在某些特定条件下才是安全的:
- 您 运行 在单个 Goroutine 中进行测试。您不能在测试中使用
t.Parallel()
。 - 您只能 运行 测试一次。否则,您必须在每次测试 运行. 后实施额外的拆卸例程以将数据重置为原始状态
- 您不能更改文件中的测试顺序。开发人员过去常常依赖于功能顺序并不重要。对于在不更改代码的情况下移动测试的人来说,对顺序的依赖可能会非常混乱。
这些只是我脑海中的几个例子。打破这些条件中的任何一个都会破坏测试。这就是为什么这样的测试称为脆弱。
检查是否可以避免这种情况。
通常这需要更改代码并引入新模式,例如 Dependency Injection。制作代码 testable
是一件好事。你让它更模块化,更容易维护。