[Lib hay mỗi tuần] Goconvey - Thay đổi cách test của bạn
Lần này mình sẽ review về một Go lib hay ho - GoConvey
TL;DR
Một fancy testing tool cho Gophers. Có hỗ trợ Browser UI và một vài concept theo mình là khá hay.
Một số đặc điểm
- Behavioral test
- Browser UI integration (cho cả go test native)
- Output rất đẹp mắt :))
- Concept về execution order thú vị giúp setup + teardown một cách tự nhiên
- Isolated test cases
Cách cài đặt
go get github.com/smartystreets/goconvey
Cách sử dụng
Giờ mình sẽ có vài cái hàm đơn giản như sau:
func Add(a, b int) int {
return a + b
}
func Subtract(a, b int) int {
return a - b
}
func Multiply(a, b int) int {
return a * b
}
func Division(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("can not divide by zero")
}
return a / b, nil
}
Giờ test nó bằng Covey xem sao.
Để sử dụng thì các bạn import . "github.com/smartystreets/goconvey/convey"
.
Dấu “.” ở đây cho phép được dùng thẳng function trong convey luôn mà không cần gọi thông qua identifier (gọi thẳng Convey thay vì convey.Convey). Code sẽ nhìn clear hơn.
Test thử hàm Add
func TestAdd(t *testing.T) {
Convey("Add two numbers", t, func() {
So(Add(1, 2), ShouldEqual, 3)
})
}
Một function test sẽ bắt đầu bằng từ khóa Convey
. Ở trên hàm TestAdd sẽ thử dùng hàm Add cộng 2 số 1 và 2 và test xem thử có đúng bằng 3 không.
Việc assert ở đây là thông qua từ khóa So
.
Các bạn có thể thấy giống như viết văn vậy: So Add(1,2) should equal 3.
Viết thử hàm khó hơn xem. Hàm chia. Hàm chia thì mình cần phải xét thêm 1 điều kiện là chia cho 0. Hàm test của chúng ta có nội dung như sau.
func TestDivision(t *testing.T) {
Convey("Divide one by another", t, func() {
Convey("Divide by non-zero number", func() {
num, err := Division(10, 2)
So(err, ShouldBeNil)
So(num, ShouldEqual, 5)
})
Convey("Divide by zero", func() {
_, err := Division(10, 0)
So(err, ShouldNotBeNil)
})
})
}
Dễ hiểu quá đúng không?
Khi chạy chúng ta chạy bằng lệnh go test builtin của go. Kết quả sẽ được:
Với mỗi So
pass chúng ta sẽ được 1 dấu tick. Khi bạn viết assert nhiều value thì sẽ ra nhiều dấu tick rất đẹp mắt. So
fail thì sẽ ra dấu X
.
Goconvey ngoài ra còn giúp chúng ta visualize testcases thông qua web portal.
Các bạn chạy command goconvey
. Nó sẽ đưa các bạn đến trình duyệt ở địa chỉ http://127.0.0.1:8080/.
So cool!
Execution Order
Một điểm đặc biệt sẽ khiến hầu hết các bạn what the heck trong lần đầu tiên dùng convey là: “Quái sao cái giá trị của mình nó lúc thế này lúc thế kia vậy???”
Lấy một ví dụ nhé
func TestSomething(t *testing.T) {
Convey("Prepare", t, func() {
a := 1
b := 2
Convey("Add 1", func() {
a = a + 1
So(a, ShouldEqual, 2)
b = b + 1
So(b, ShouldEqual, 3)
})
Convey("Subtract 1 ", func() {
a = a - 1
So(a, ShouldEqual, ??)
b = b - 1
So(b, ShouldEqual, ??)
})
})
}
Theo các bạn thì ví dụ ở trên giá trị tương ứng với 2 dấu ??
là gì?
Nhìn thì trên xuống, a = 1, b = 2. Sau khi đi qua hàm Add 1
thì a = 2, b = 3. Sau khi đi qua Subtract 1
thì a lại về 1 và b về 2 đúng không?
Hoàn toàn không. Kết quả sẽ là a = 0 và b = 1.
… What the heck? Giá trị của a
unexpected. Well xin giới thiệu với các bạn, execution order của Convey:
Ví dụ có structure convey như sau
Convey A
So 1
Convey B
So 2
Convey C
So 3
Thứ tự thực hiện không phải là A1B2C3
nhé. Mà là A1B2A1C3
. :))
Convey có thứ tự execution như vậy là để hỗ trợ setup và tear down một cách tự nhiên hơn.
Convey("Create", func() {
Convey("Success", func() {
accountID := uuid.NewV4().String()
role := randomRole(accountID, randomdata.StringNumber(4, "-"))
_, err = core.CreateRole(role)
So(err, ShouldBeNil)
Reset(func() {
arango.TruncateCollectionForTesting(
cArango.RoleCollection,
)
})
Convey("AlreadyExist", func() {
role.ID = ""
_, err := core.CreateRole(role)
So(err, ShouldEqual, ErrRoleAlreadyExists)
})
Convey("GetByID", func() {
r, err := core.GetRoleByID(role.ID)
So(err, ShouldBeNil)
So(r, ShouldResemble, role)
})
Convey("GetByType", func() {
r, err := core.GetRoleByType(role.Type)
So(err, ShouldBeNil)
So(r, ShouldResemble, role)
})
})
})
Ví dụ như đoạn code trên, việc setup teardown của mình chỉ cần khai báo một lần. Và các Convey phía sau sẽ có isolated context, giúp cho các test cases của mình không depend lẫn nhau.
Tổng kết
Xài Convey đi.
Runi blog
Runi is a gopher