昨天說完了基礎的單元測試,今天讓我們好好的了解如何透過更好的方式來進行單元測試。
從昨天的範例中可以看出,如果我們要比對結果是否正確必須要自己寫 if/else 來判斷,這樣程式並不簡潔,當然我們可以自行封裝方法,但已經有大神幫我們寫好的專屬的測試庫 - testify
透過 go get 的方式進行安裝
go get -u github.com/stretchr/testify
我們這邊來改寫昨天的單元測試方法
func TestAdd(t *testing.T) {
	for i, in := range input {
		result := Add(in[0], in[1])
		t.Log("in =", in, "output should be", output[i], " actual output equal", result)
		if output[i] == result {
			t.Log("Pass")
		} else {
			t.Error("Case", i, "is failed")
		}
	}
}
首先我們匯入 testify 中的 require 模組
import "github.com/stretchr/testify/require"
接著我們將原先的 if 取代成 require.Equal 方法,testify 幫我們封裝了許多好用的方法,包括了 Equal、Contains、Empty 等等,讓我們可以不用再寫 if 來判斷
func TestAdd(t *testing.T) {
	for i, in := range input {
		result := Add(in[0], in[1])
		t.Log("in =", in, "output should be", output[i], " actual output equal", result)
		require.Equal(t, output[i], result)
	}
}
suite 是由 testify 所提供的 package,他可以幫我們針對每個測試案例做 前置作業 與 後置操作,讓我們可以更完整的掌握要測試案例的生命週期。
首先我們定義一個 struct 名為 Suite,在這個結構裡面繼承了 suite 的 Suite。
type Suite struct {
	suite.Suite
	finalTest   int
	finalResult int
	testInput   [][]int
	testOutput  []int
}
suite 有提供 SetupSuite 方法讓我們實作,因此可以在此方法內進行參數的初始化,在這邊我們先將測試的參數進行初始化。
func (s *Suite) SetupSuite() {
	s.testInput = [][]int{
		{1, 2},
		{2, 3},
		{3, 4},
	}
	s.testOutput = []int{3, 5, 7}
	s.finalTest = 0
	for _, n := range s.testOutput {
		s.finalResult += n
	}
}
要使用 suite 來做單元測試,我們可以在剛才建立的 Suite struct 中實現方法,如果要測試 Add 方法的話,就實作 TestAdd。
func (s *Suite) TestAdd() {
	for i, in := range s.testInput {
		result := Add(in[0], in[1])
		s.finalTest += result
		require.Equal(s.T(), s.testOutput[i], result, "should be equal")
	}
}
在測試案例測完的時候,可以實作 AfterTest 方法來檢查此測試用例是否有誤。
func (s *Suite) AfterTest(_, _ string) {
	require.Equal(s.T(), s.finalResult, s.finalTest, "should be equal")
}
透過實作 TestStart 方法來設定起始點,裡面就使用 suite.Run 的方式將 Suite struct 進行初始化。
func TestStart(t *testing.T) {
	suite.Run(t, new(Suite))
}
將上述的程式集合起來程式碼如下
package cal
import (
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
	"testing"
)
type Suite struct {
	suite.Suite
	finalTest   int
	finalResult int
	testInput   [][]int
	testOutput  []int
}
func (s *Suite) SetupSuite() {
	s.testInput = [][]int{
		{1, 2},
		{2, 3},
		{3, 4},
	}
	s.testOutput = []int{3, 5, 7}
	s.finalTest = 0
	for _, n := range s.testOutput {
		s.finalResult += n
	}
}
//AfterTest 用於測試完畢之後的檢查
func (s *Suite) AfterTest(_, _ string) {
	require.Equal(s.T(), s.finalResult, s.finalTest, "should be equal")
}
//TestStart 為測試程式進入點
func TestStart(t *testing.T) {
	suite.Run(t, new(Suite))
}
func (s *Suite) TestAdd() {
	for i, in := range s.testInput {
		result := Add(in[0], in[1])
		s.finalTest += result
		require.Equal(s.T(), s.testOutput[i], result, "should be equal")
	}
}
依照正確執行,執行結果如下
=== RUN   TestStart
=== RUN   TestStart/TestAdd
--- PASS: TestStart (0.00s)
    --- PASS: TestStart/TestAdd (0.00s)
PASS
我們可以透過修改 SetupSuite 將測試結果改成錯誤的
func (s *Suite) SetupSuite() {
	s.testInput = [][]int{
		{1, 2},
		{2, 3},
		{3, 4},
	}
	s.testOutput = []int{3, 5, 8}
	s.finalTest = 0
	for _, n := range s.testOutput {
		s.finalResult += n
	}
}
接著重新運行一次測試程式,顯示結果如下
=== RUN   TestStart
=== RUN   TestStart/TestAdd
    TestStart/TestAdd: cal_test.go:44: 
        	Error Trace:	cal_test.go:44
        	Error:      	Not equal: 
        	            	expected: 8
        	            	actual  : 7
        	Test:       	TestStart/TestAdd
        	Messages:   	should be equal
    TestStart/TestAdd: cal_test.go:33: 
        	Error Trace:	cal_test.go:33
        	            				suite.go:137
        	            				panic.go:617
        	            				testing.go:657
        	            				cal_test.go:44
        	Error:      	Not equal: 
        	            	expected: 16
        	            	actual  : 15
        	Test:       	TestStart/TestAdd
        	Messages:   	should be equal
--- FAIL: TestStart (0.00s)
    --- FAIL: TestStart/TestAdd (0.00s)
Expected :16
Actual   :15
<Click to see difference>
FAIL
從上述結果可以看到,他會顯示預期結果與實際運行結果不符,顯示相關的訊息,這樣的方式可以讓我們更好查出錯誤在哪。
今天透過簡單介紹 testify 這個單元測試的 package,讓我們在撰寫單元測試可以更快速且精準。