如何使用Go中的测试包进行测试设置

mil*_*onb 82 unit-testing go

我如何进行整体测试设置处理,为使用测试包时的所有测试设置阶段?

作为Nunit的一个例子,有一个[SetUp]属性.

[TestFixture]
public class SuccessTests
{
  [SetUp] public void Init()
  { /* Load test data */ }
}
Run Code Online (Sandbox Code Playgroud)

Sal*_*ali 127

从Go 1.4开始,您可以实现设置/拆卸(无需在每次测试之前/之后复制您的功能).该文档概括这里主要部分:

TestMain在主goroutine中运行,可以执行任何设置,并且在调用m.Run时需要拆卸.然后应该使用m.Run的结果调用os.Exit

我花了一些时间才弄清楚这意味着如果测试包含一个函数,func TestMain(m *testing.M)那么将调用此函数而不是运行测试.在这个函数中,我可以定义测试的运行方式.例如,我可以实现全局设置和拆解:

func TestMain(m *testing.M) {
    setup()
    code := m.Run() 
    shutdown()
    os.Exit(code)
}
Run Code Online (Sandbox Code Playgroud)

其他几个例子可以在这里找到.

  • `TestMain`曾经是一个包,所以它没那么有用.我发现[subtests](https://blog-test.golang.org/subtests)更适合更复杂的用途. (11认同)
  • 答案中不清楚安装/拆卸是针对每个测试函数运行还是仅针对包运行一次。 (4认同)
  • 请注意,' code := m.Run() ' 是运行其他 TestFunctions 的函数! (3认同)
  • 您应该如何在不使用全局变量的情况下将上下文从设置函数传递到测试?例如,如果mySetupFunction()创建一个临时目录来执行测试(使用唯一的随机名称),那么测试如何知道目录的名称?必须有一个设置此上下文的地方? (2认同)
  • 似乎这是处理测试前后挂钩的官方方法,请参阅 https://golang.org/pkg/testing/#hdr-Main 以获取官方文档 (2认同)
  • @InancGumus`lstat $ GOROOT / subtests:没有这样的文件或目录` (2认同)

mil*_*onb 37

这可以通过init()_test.go文件中放入一个函数来实现.这将在init()函数之前运行.

// package_test.go
package main

func init() {
     /* load test data */
}
Run Code Online (Sandbox Code Playgroud)

_test.init()将在包init()函数之前调用.

  • 你犯错了吗?它说“init()”函数将在“init()”函数之前运行,我认为这首先是不可能的,但我怀疑你的意思是在任何测试函数之前运行? (5认同)
  • 它不包括拆卸部分 (3认同)
  • 我知道你正在回答你自己的问题,所以这可能满足你自己的用例,但这并不等同于你在问题中包含的NUnit示例. (2认同)
  • 很公平.你在这个答案中所展示的内容更接近于使用NUnit的`[TestFixtureSetUp]`属性. (2认同)
  • 如果您的测试文件与主要功能位于同一软件包中,则这不是一个好的解决方案。 (2认同)

小智 26

使用以下模板,您可以在每个执行设置和拆卸的测试方法中进行一行调用。

func setupTest() func() {
    // Setup code here

    // tear down later
    return func() {
        // tear-down code here
    }
}

func TestMethod(t *testing.T) {
    defer setupTest()()
    // asserts, ensures, requires... here
}
Run Code Online (Sandbox Code Playgroud)


Kar*_*eva 23

给定单元测试的简单功能:

package math

func Sum(a, b int) int {
    return a + b
}
Run Code Online (Sandbox Code Playgroud)

您可以使用返回拆卸功能的设置功能对其进行测试.在调用setup()之后,您可以对teardown()进行延迟调用.

package math

import "testing"

func setupTestCase(t *testing.T) func(t *testing.T) {
    t.Log("setup test case")
    return func(t *testing.T) {
        t.Log("teardown test case")
    }
}

func setupSubTest(t *testing.T) func(t *testing.T) {
    t.Log("setup sub test")
    return func(t *testing.T) {
        t.Log("teardown sub test")
    }
}

func TestAddition(t *testing.T) {
    cases := []struct {
        name     string
        a        int
        b        int
        expected int
    }{
        {"add", 2, 2, 4},
        {"minus", 0, -2, -2},
        {"zero", 0, 0, 0},
    }

    teardownTestCase := setupTestCase(t)
    defer teardownTestCase(t)

    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            teardownSubTest := setupSubTest(t)
            defer teardownSubTest(t)

            result := Sum(tc.a, tc.b)
            if result != tc.expected {
                t.Fatalf("expected sum %v, but got %v", tc.expected, result)
            }
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

Go测试工具将在shell控制台中报告日志记录语句:

% go test -v
=== RUN   TestAddition
=== RUN   TestAddition/add
=== RUN   TestAddition/minus
=== RUN   TestAddition/zero
--- PASS: TestAddition (0.00s)
    math_test.go:6: setup test case
    --- PASS: TestAddition/add (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/minus (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/zero (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    math_test.go:8: teardown test case
PASS
ok      github.com/kare/go-unit-test-setup-teardown 0.010s
% 
Run Code Online (Sandbox Code Playgroud)

您可以使用此方法将一些其他参数传递给setup/teardown.

  • 现在这是一个真正简单但有效的技巧.很好地使用Go语法. (2认同)
  • 是的,但它增加了嵌套性(_javascript 中的厄运金字塔_)。而且,测试不会像外部测试那样由套件自动运行。 (2认同)

Pau*_*kin 12

通常,go中的测试不会以与其他语言相同的样式编写.通常,测试函数相对较少,但每个都包含一组由表驱动的测试用例.请参阅Go团队之一撰写的这篇文章.

使用表驱动测试,您只需在执行表中指定的各个测试用例的循环之前放置任何设置代码,然后再放入任何清理代码.

如果你仍然在测试函数之间有共享设置代码,你可以将共享设置代码提取到一个函数中,并使用一个sync.Once重要的是它只执行一次(或者作为另一个答案暗示,使用init(),但这有一个缺点,即设置即使测试用例没有运行也会完成(也许是因为你通过使用限制了测试用例go test -run <regexp>.)

我想如果你认为你需要在不同的测试之间进行共享设置,只要你真的需要它就应该进行一次思考,并且如果表驱动的测试不会更好.

  • 在测试诸如标志解析器之类的琐碎事物或者用于搅动数字的算法时,这非常棒.但是在尝试测试所有需要类似样板代码的各种功能时,它并没有真正帮助.我想我可以在一个数组中定义我的测试函数并迭代它们,但是它实际上不是表驱动的,而是一个简单的循环,它应该真正构建到测试框架本身(以适当的测试套件的形式)具有设置/拆卸功能) (4认同)

Jam*_*dge 9

Go测试框架没有任何与NUnit的SetUp属性等价的东西(标记在套件中的每个测试之前调用的函数).但有几个选择:

  1. 只需SetUp在需要的每个测试中调用您的函数.

  2. 使用Go的测试框架的扩展来实现xUnit范例和概念.我想到了三个强大的选择:

这些库中的每一个都鼓励您将测试组织到类似于其他xUnit框架的套件/夹具中,并在每个Test*方法之前调用套件/夹具类型的设置方法.


Jkn*_*air 6

如果有人正在寻找 @BeforeEach (在测试文件中的每个测试之前运行)和 @AfterEach (在测试文件中的测试之后运行)的替代方案,这里有一个帮助程序片段。

func CreateForEach(setUp func(), tearDown func()) func(func()) {
    return func(testFunc func()) {
        setUp()
        testFunc()
        tearDown()
    }
}

Run Code Online (Sandbox Code Playgroud)

您可以在 TestMain 的帮助下像下面这样使用它

var RunTest = CreateForEach(setUp, tearDown)

func setUp() {
   // SETUP METHOD WHICH IS REQUIRED TO RUN FOR EACH TEST METHOD
   // your code here
}

func tearDown() {
  // TEAR DOWN METHOD WHICH IS REQUIRED TO RUN FOR EACH TEST METHOD
  // your code here
}

fun TestSample(t *testing.T) {
  RunTest(func() {
    // YOUR CODE HERE
  })
}
Run Code Online (Sandbox Code Playgroud)

你也可以检查:go-beforeeach