Go 重構筆記 1 - Extract Method

一開始先來一個簡單的。

Background

有一系列的測試開頭都有這段一樣的前置 code:

  1. Create gomock controller
  2. Create temp dir
  3. Assert mocks is invoked
  4. Remove temp dir
package test

import (
    "io/ioutil"
    "os"
    "testing"

    "github.com/golang/mock/gomock"
)

func TestFoo(t *testing.T)  {
    ctrl := gomock.NewController(t)
    dir, err := ioutil.TempDir("", "")
    if err != nil {
        t.Fatalf("could not create tmp directory: %v", err)
    }
    defer func(ctrl *gomock.Controller, dir string) {
        ctrl.Finish()
        os.RemoveAll(dir)
    }(ctrl, dir)

    // do something
}

func TestBar(t *testing.T)  {
    ctrl := gomock.NewController(t)
    dir, err := ioutil.TempDir("", "")
    if err != nil {
        t.Fatalf("could not create tmp directory: %v", err)
    }
    defer func(ctrl *gomock.Controller, dir string) {
        ctrl.Finish()
        os.RemoveAll(dir)
    }(ctrl, dir)

    // do something
}

每次要新增一個 test case 就要寫一大堆漏漏長,越寫心情越差,可讀性低, 也不 DRY,還很 WET不好笑

Solution

其實這邊就是在做 unit test 的 setupteardown 的部分

  1. Create gomock controller setup
  2. Create temp dir setup
  3. Assert mocks is invoked teardown
  4. Remove temp dir teardown

我們把 setup 的部分抽成一個 func setup(),但因為 teardownsetup 有依賴,不能抽成兩個 func, 改成在 setup() 先包成一個 callback 回傳,之後在由 test case 各自 defer teardown()

package test

import (
    "io/ioutil"
    "os"
    "testing"

    "github.com/golang/mock/gomock"
)

func setup(t *testing.T) (*gomock.Controller, string, func()) {
    ctrl := gomock.NewController(t)
    dir, err := ioutil.TempDir("", "")
    if err != nil {
        t.Fatalf("could not create tmp directory: %v", err)
    }
    teardown := func() {
        ctrl.Finish()
        os.RemoveAll(dir)
    }

    return ctrl, dir, teardown
}

func TestFoo(t *testing.T)  {
    ctrl, dir, teardown := setup(t)
    defer teardown()

    // do something
}

func TestBar(t *testing.T)  {
    ctrl, dir, teardown := setup(t)
    defer teardown()

    // do something
}

喔喔喔是不是乾淨多了?也可以很清楚知道這段 code 是在做什麼。