Go 测试中如何实现 setUp 和 tearDown 功能

2024-01-22
#go

前言

在实际写 Go 的单元测试过程中,我们有时候需要在执行测试之前调用 setUp 函数(加载配置完成初始化操作)和测试执行之后执行 tearDown 函数(关闭释放连接或者资源),通过可以分为以下三个情况:

  1. 所有测试用例一起执行前后
  2. 分组测试用例执行前后
  3. 单个测试用例执行前后

所有测试用例执行前后

Go 的 testing 的标准库里提供了一个 TestMain 的方法

  func TestMain(m *testing.M)

一个最小的实现如下:

  func setUp() {
    // set up
  }
  
  func tearDown() {
    // tear down
  }
  
  func TestMain(m *testing.M) {
    setUp()
    
    code := m.Run()
    
    tearDown()
    
    os.Exist(code)
  }

就像最先说到的那样,我们通常会在全局的 setUp 里完成一些资源的连接或者加载,完成一些其他测试用例需要使用的共同的实例的初始化,然后在 tearDown 完成资源的释放和回收

分组测试用例执行前后

有时候我们会进行分组测试,这个时候配合 t.Run 使用完成 subtest,对测试用例进行归类,与全局的 setUp 和 tearDown 不同的是,在这种场景下每个分组需要的资源可能略微不同,这个时候我们就需要有差异化的初始化了,一个典型的例子如下:

  func TestSomethingWhenGivenCondition(t *testing.T) {
    // set up
    
    t.Run("特性1", func(t *testing.T) {
      
    })
    t.Run("特性2", func(t *testing.T) {
      // Subtest 2 code goes here
    })
   
    // tear down
  }

单个测试用例执行前后

go 的 test 标准库并没有提供在每个测试用例执行前后的函数,对于这种场景,就需要我们自己构建对应的函数去完成了,一个最小的实现方法通常如下:

  func setupTestCase(t *testing.T) func(t *testing.T) {
  	// set up 
    	// code 
  	return func(t *testing.T) {
  	   // code
        // tear down
  	}
  }
  
  func TestA(t *testing.T) {
  	teardownTestCase := setupTestCase(t)
  	defer teardownTestCase(t)
  	// code
  }

每次测试用例执行前后的时候,对应的 set up 的 tear down 的代码都会被执行。除此之外,也有借助 t.Run 去实现这一目的的

  func testCase(test func(t *testing.T, c *testContext)) func(*testing.T) {
    return func(t *testing.T) {
      context := &testContext{}
      context.beforeEach()
      defer context.afterEach()
      test(t, context)
    }
  }
  
  func TestSomething(t *testing.T) {
    t.Run("Some test case", testCase(func(t *testing.T, c *testContext) {
      // Test code goes here which can leverage the context
    }))
  }

参考