เขียน test บน go ด้วย testify
จริง ๆ ใน golang มี package ชื่อ testing อยู่ ใช้ตัวนั้นก็ได้ ตรงไปตรงมาดี ความซับซ้อนก็จะเริ่มมาตอนที่เราอยากจะทำอะไรที่ยากขึ้น เช่น การทำ test double หรือการเปรียบเทียบ struct 2 ตัว ซึ่งจริง ๆ แล้วเราสามารถเอาพวก reflect มาใช้เปรียบเทียบ struct ได้ ส่วนการทำ test double ก็จะซับซ้อนกว่านั้น
testify เป็น package set ที่เตรียมของพวกนี้ไว้ให้เราใช้ test package ของเราง่ายขึ้น
ใน testify มี package ย่อย ๆ ให้เราใช้หลายตัว
assert
ปกติถ้าเราใช้แค่ testing package ตอนเราอยากจะ assert อะไร เราก็ใช้การเปรียบเทียบธรรมดา เช่น
if res != “OK” {
t.Errorf(“expecting result to be OK but %v”, res)
}
แต่ใน testify เราสามารถเขียนได้ด้วย
assert.Equal(t, “OK”, res, “expecting result to be OK”)
ที่สำคัญ ถ้าค่าที่เราต้องการเปรียบเทียบเป็น struct โดยปกติเราจะใช้ reflect.DeepEqual ในการเปรียบเทียบ เช่น
if !reflect.DeepEqual(exp, res) {
t.Errorf(“expecting result to be %v but %v”, exp, res)
}
แต่ตัว assert.Equal เราสามารถส่ง struct object เข้าไปได้เลย แถมตอนที่เปรียบเทียบ struct object มันยังบอกจุดที่แตกต่างกันให้อีก ไม่ต้องมานั่งลูปหาเองว่าต่างกันตรงไหน เช่น
package my_testimport (
"testing""github.com/stretchr/testify/assert"
)type Address struct {
Street string
PostCode string
}type Person struct {
Name string
Age int
Nationality string
Address
}func TestSomething(t *testing.T) {
assert.Equal(t, Person{
Name: "a",
Age: 17,
Nationality: "Thai",
Address: Address{
Street: "123 Abc",
PostCode: "12370",
},
}, Person{
Name: "b",
Age: 24,
Nationality: "Thai",
Address: Address{
Street: "345 Back",
PostCode: "12370",
},
}, "they should be equal")
}
ในตัว assert package เองมี method ที่ช่วยให้เรา assert ของได้ง่ายขึ้นอีกหลายตัว สามารถเข้าไปดู method ทั้งหมดได้ที่ https://godoc.org/github.com/stretchr/testify/assert
require
มี method เหมือน assert แต่แค่ถ้า fail มันจะหยุด test ทันที
mock
ทำเหมือนการทำ dependency injection ทั่วไป คือ เราจะสร้าง mock จาก interface ดังนั้นสร้าง interface ไว้ก่อน และสร้าง struct ที่มี method ที่ implement interface นั้น เป็น code ที่ทำงานจริง ๆ แล้วก็ decouple variable ของที่อยู่ใน struct หรือ package แล้ว inject interface นั้นเข้ามาในฐานะ dependency แทน
ตอนเขียน mock ใน test ก็ทำประมาณนี้
type mockedObject struct {
mock.Mock
}// implement method ใน interface
func (m *mockedObject) DoSomething(number int) (bool, error) {
// track behavior เพื่อ spy
args := m.Called(number)
// return ของตามที่ประกาศไว้ใน stub
return args.Bool(0), args.Error(1)
}
ตอนเขียน test ก็ประมาณนี้
func TestSomething(t *testing.T) {
// สร้าง mock
o := new(mockedObject) // spy
o.On(“DoSomething”, 123).Return(true, nil) // action
MethodUnderTest(o) // assert ว่ามีการเรียกของที่ spy ไว้มั๊ย
o.AssertExpectations(t)
}
ใน mock package มีของให้เล่นอีกเพียบ ลองเข้าไปดูกันได้ที่ https://godoc.org/github.com/stretchr/testify/mock
suite
ใช้จับกลุ่ม test cases ในไฟล์เป็น test suite เช่น
// ประกาศ test suite และตัวแปรที่ใช้ใน test suite
type TestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}// test setup ประกาศให้ตัวแปรมีค่าเป็น 5
func (suite *TestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}// test case จะขึ้นต้นด้วยคำว่า Test
func (suite *TestSuite) TestExample() {
assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
}// สร้าง test wrapper มาครอบ test suite ไว้ เพื่อให้ตอนสั่ง go test มันมาเรียก test suite ของเราได้
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(TestSuite))
}
เข้าไปดู method อื่น ๆ ของ suite เช่นพวก suite setup, suite teardown ได้ที่ https://godoc.org/github.com/stretchr/testify/suite
ทีนี้เขียน test ง่ายขึ้นอีกเยอะเลย ใครอยากลองใช้เข้าไปดูได้ที่ https://github.com/stretchr/testify เลยครับ
pingback: http://www.chonla.com/write-golang-unit-test-with-testify/