为了账号安全,请及时绑定邮箱和手机立即绑定

Test_xxx功能是否安全,无法访问 golang 中的共享数据?

Test_xxx功能是否安全,无法访问 golang 中的共享数据?

Go
当年话下 2022-09-12 15:44:23
我对戈朗单元测试感到困惑。我有 2 个 funcs ,喜欢 和 。在,i将更改一个全局变量,可以看到更改吗?Test_xxxTest_1Test_2Test_1Test_2此外,如果我使用而不是更改全局 var,其他功能是否会感知到修补?即,我是否有必要在返回时使用延迟来取消功能替换?monkey patchTest_xxxTest_xxx
查看完整描述

5 回答

?
慕的地10843

TA贡献1785条经验 获得超8个赞

func 在 golang 中访问共享数据是否安全?Test_xxx

答案完全取决于是否允许这些测试函数并行运行。

默认情况下,按顺序调用给定包的测试函数。但是,如果go test

  • 在两个测试函数中调用 t.Parallel(),并且

  • 两个函数访问(写/写或写/读)相同的全局变量,它们之间没有任何同步,

你可能会得到一场数据竞赛。


要修复想法,请考虑以下简单测试文件:

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

这应该使您确信在测试中处理全局状态时应该小心。如果可以的话,最好的办法是完全避免全局状态。或者,请记住,在激活并行测试执行后立即,必须注意同步对全局状态的访问。


查看完整回答
反对 回复 2022-09-12
?
饮歌长啸

TA贡献1951条经验 获得超3个赞

Test_1,我将更改一个全局变量,Test_2可以看到更改吗?


是的。


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

我有必要在Test_xxx返回时使用延迟来获取功能子结构吗?


您可以使用清理功能删除全局变量的更改


func Test_1(t *testing.T) {

    t.Cleanup(func() {

        global = 0

    })

    for i := 0; i < 1000; i++ {

        global++

    }

    fmt.Println(global)

}

PLAYGROUND


查看完整回答
反对 回复 2022-09-12
?
千巷猫影

TA贡献1829条经验 获得超7个赞

虽然这是可能的,但您可能需要考虑从两个测试初始化全局变量,以生成一致的行为。从命令行运行测试时,可以选择仅运行一个测试函数 (),否则将生成不一致的结果。gogo test foo -test.run Test_1


访问全局变量受各种竞赛的影响(在某些情况下涉及部分读取数据,因为它在其他地方被覆盖)返回无意义/不可能的值!使用某种方式来防止这些种族: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


查看完整回答
反对 回复 2022-09-12
?
胡说叔叔

TA贡献1804条经验 获得超8个赞

Test_1,我将更改一个全局变量,Test_2可以看到更改吗?

它仅在某些特定条件下才是安全的:

  1. 您在单个戈鲁廷中运行测试。不能在测试中使用。t.Parallel()

  2. 您只能运行一次测试。否则,您必须实现额外的拆卸例程,以便在每次测试运行后将数据重置为其原始状态。

  3. 您无法更改文件中的测试顺序。开发人员过去依赖函数顺序并不重要。对于在不更改其代码的情况下移动测试的人来说,对顺序的依赖可能会非常混乱。

这些只是我脑海中的几个例子。打破这些条件中的任何一个都会破坏测试。这就是为什么这种测试被称为脆弱。

检查是否可以避免这种情况。

这通常需要更改代码并引入新的模式,如依赖注入。编写代码是一件好事。您使它更加模块化,更易于维护。testable


查看完整回答
反对 回复 2022-09-12
?
紫衣仙女

TA贡献1839条经验 获得超15个赞

请注意,未来的 Go 版本可能会更改测试的运行顺序,例如,通过随机化顺序。如果您的测试依赖于之前运行的事实并更改了全局变量,则它将中断。Test_1Test_2


更改全局变量的一个好习语是这样的:


func Test_1(t *testing.T) {

  oldVal := myGlobalVariable

  defer func() { myGlobalVariable = oldVal }

  // rest of the test

}


查看完整回答
反对 回复 2022-09-12
  • 5 回答
  • 0 关注
  • 79 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信