Published on

Golang Part 3: Go Testing

Authors

Go Testing

เมื่อเราเขียนโปรแกรมแล้ว ก็ต้องมีการทดสอบโปรแกรมที่เราเขียนขึ้นมา ซึ่งการเขียน Test ในภาษา go นั้น เป็นเรื่องง่ายมากๆ เพราะ Go นั้นได้ build in มาให้แล้ว ซึ่งสามารถทำได้ทั้งการเขียน Test, Benchmark และเขียน Example เพื่อทำแสดงตัวอย่างการเรียกใช้งานฟัง์ชันนั้นๆ ได้

Test

การเขียน Test ในภาษา go นั้นมีหลักการเขียนมีดังนี้

  1. ชื่อของไฟล์จะต้องลงท้ายด้วย _test.go

  2. ฟังก์ชันของ unit test จะต้องขึ้นต้นคำว่า Test และควรตั้งให้อ่านแล้วเข้าใจ

  3. ฟังก์ชันของ unit test จะต้องรับพารามิเตอร์ 1 ตัว คือ (t *testing.T)

  4. (Optional) packace name จะลงท้ายด้วย _test หรือไม่ก็ได้

  5. เขียน test และเพื่อให้ง่ายต่อการเขียนจะใช้หลักการ AAA คือ Arrange Act และ Assert

    Arrange สำหรับกำหนดค่าเริ่มต้นของการทดสอบ เช่น กำหนด input กำหนดผลลัพธ์ที่ต้องการ หรือ mock function ต่างๆ

    Act สำหรับ execute (เรียก function) ส่วนที่ต้องการทดสอบหรือ business logic

    Assert สำหรับการตรวจสอบว่า ผลการทำงานตรงตามที่คาดหวังหรือไม่ ซึ่งต้องมีทุก test

ตัวอย่างจะทำการเขียน Unit Test ทดสอบโปรแกรมบวก และลบ ง่ายๆ

  • สร้างโปรเจคใหม่
$ mkdir gotest
$ cd gotest
$ go mod init gotest
  • ตัวอย่างโปรแกรม
math/math.go
package math

// Add is our function that sums two integers
func Add(x, y int) (res int) {
	return x + y
}

// Subtract subtracts two integers
func Subtract(x, y int) (res int) {
	return x - y
}
  • เขียน Unit Test
math/math_test.go
package math

import "testing"

func TestAdd(t *testing.T){
  // Arrange
  input1, input2 := 4, 6
  want := 10

  // Act
  got := Add(input1, input2)

  // Assert
  if got != want {
      t.Errorf("got %q, wanted %q", got, want)
  }
}

การรัน Unit Test

การรัน test ทำได้โดยใช้คำสั่ง go test [package_name]

$ go test gotest/math
PASS
ok      gotest/math     0.252s

หรือจะการรันทุก test ตั้งแต่ directory ปัจจุบันลงไป ใช้คำสั่ง go test ./...

$ go test ./...
PASS
ok      gotest/math     0.252s

หรือจะการรันทุก test ใน ./math

$ cd math
$ go test
PASS
ok      gotest/math     0.244s

ถ้าต้องการให้แสดงรายละเอียดออกมา ให้เพิ่ม -v

$ go test -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok      gotest/math     0.254s

การเช็ค Code coverage

ถ้าต้องการตรวจสอบว่าเราเขียน test ครอบคลุมโค้ดทั้งหมดแล้วหรือไม่ ด้วยการเพิ่ม -cover เข้าไป

$ cd math
$ go test -cover
PASS
coverage: 50.0% of statements
ok      gotest/math     0.254s

และเรายังสามารถส่งออกเป็นไฟล์ เพื่อนำไปดูรายละเอียดเพิ่มเติมได้

$ go test "-coverprofile=coverage.out"
coverage: 50.0% of statements
ok      gotest/math     0.280s
  • ดูรายละเอียดว่าแต่ละ function นั้น มี test coverage เท่าไหร่
$ go tool cover "-func=coverage.out"
gotest/math/math.go:4:  Add             100.0%
gotest/math/math.go:9:  Subtract        0.0%
total:                  (statements)    50.0%
  • แสดงผลในรูปแบบ HTML
$ go tool cover "-html=coverage.out"
covered

Benchmark

นอกจากเขียนจะ Test เพื่อทดสอบการงานว่าถูกต้องหรือไม่นั้น ใน Go ยังสามารถเขียนเพื่อทดสอบ performance ของโค้ดได้ด้วย ซึ่งจะคล้ายกับการเขียน Test แค่เปลี่ยนชื่อฟังก์ชันให้ขึ้นต้นด้วย Benchmark แทนแบบนี้ func BenchmarkXxx(*testing.B)

ตัวอย่าง จะทดสอบ benchmark ฟังก์ชัน Add

func BenchmarkAdd(b *testing.B){
  for i :=0; i < b.N ; i++{
    Add(4, 6)
  }
}

ส่วนการรัน ให้ใส่ -bench เติมเข้าไป โดยถ้าต้องการ benchmark ทุกตัวใช้ -bench=. หรือถ้าจะระบุชื่อไปก็ได้ -bench=BenchmarkAdd

$ go test gotest/math -bench=BenchmarkAdd
goos: windows
goarch: amd64
pkg: gotest/math
cpu: Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz
BenchmarkAdd-8          1000000000               0.3443 ns/op
PASS
ok      gotest/math     0.664s

ถ้าจะทดสอบ memory ด้วย ให้เพิ่ม -benchmem เข้าไป

$ go test gotest/math -bench=BenchmarkAdd -benchmem
goos: windows
goarch: amd64
pkg: gotest/math
cpu: Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz
BenchmarkAdd-8          1000000000               0.3467 ns/op
      0 B/op           0 allocs/op
PASS
ok      gotest/math     0.676s

Document

สุดท้ายเราสามารถเขียน Document เพื่อแสดงตัวอย่างการเรียกใช้งานฟังก์ชันได้ด้วย มีวิธีเขียนดังนี้

  1. สร้างฟังก์ชันที่ขึ้นต้นด้วย Example ไม่ต้องรับ parameter อะไร
  2. เขียน Example เพื่อแสดงตัวอย่างการใช้งานฟังก์ชันของเรา
  3. ใส่ // Output: XX เพื่อแสดงตัวอย่าง output ที่จะได้ออกมา
func ExampleAdd(){
  result := Add(4, 6)
  fmt.Println(result)
  // Output: 10
}

วิธีเอาไปแสดงใน Godoc

เพื่อที่จะเอา ExampleAdd ไปแสดงใน godoc ต้องติดตั้ง godoc ก่อน

$ go install golang.org/x/tools/cmd/godoc

แล้วรัน godoc

$ godoc -http=:8000

เมื่อลองไปเปิดดูจะเห็นว่าฟังก์ชัน Add ของเรา นั้นมีตัวอย่างการใช้งานให้ด้วย

Godoc

ก็จบแล้วกับบทความสุดท้ายของชุดการศึกษาภาษา Go ตั้งแต่พื้นฐาน จนมาจบที่การเขียน Test ซึ่งเราจะเอาความรู้ทั้งหมดนี้ ไปลองเขียนเป็น API Service กันในบทความถัดไปๆ