ตอนนี้มาปูพื้นฐานความเข้าใจโครงสร้าง file และ directory ของ Go project กัน
โจทย์ตัวอย่างเราจะมี Main Program 1 ตัว และมี Library อีก 1 ตัว
สามารถ download source code ได้ที่:
https://github.com/shall-we-go/stringutil
https://github.com/shall-we-go/reverse-hello-world
โครงสร้าง file และ directory
$GOPATH/ src/ github.com/ shall-we-go/ stringutil/ .git/ reverse.go reverse_test.go reverse-hello-world/ .git/ main.go
ข้อสังเกตุ
- โครงสร้าง และ directory จะสัมพันธ์กับ URL ของ Git repo จากตัวอย่างเราจะมี repo อยู่ที่ https://github.com/shall-we-go/stringutil และ https://github.com/shall-we-go/reverse-hello-world
- ไฟล์ Test จะต้องตั้งชื่อเป็น *_test.go โดย prefix ไม่ได้มีความเกี่ยวข้องอะไรกับชื่อไฟล์ ของ code ที่จะ test แต่การตั้งชื่อให้สอดคล้องกัน เช่น reverse.go กับ reverse_test.go จะทำให้ project ดูเป็นระเบียบเรียบร้อย แยกแยะได้ง่ายว่าไฟล์ไหน test ไฟล์ code ไหน
- ไฟล์ Test จะอยู่ package เดียวกับ ไฟล์ code โดยจริงๆสามารถอยู่คนละ package กันก็ได้ แต่ถ้าอยู่ package เดียวกันจะสะดวกกว่าเพราะไม่ต้อง import package ที่จะถูก test เข้ามา และสามารถเรียกใช้ function นั้นตรงๆได้เลยโดยไม่ต้องระบุ package นอกจากนี้เวลาสั่ง Go test tool เช่น
$ go test github.com/shall-we-go/stringutil
ก็ลดความสับสนว่าเรากำลังจะ run test ของ package ไหน
ลองทดสอบโดยใช้คำสั่งนี้ $ go run github.com/shall-we-go/reverse-hello-world
ถ้าไม่มีอะไรผิดพลาดจะได้
Hello World
ทีนี้มาทำความเข้าใจ code กันบ้าง
Library
reverse.go
1 2 3 4 5 6 7 8 9 10 11 12 | package stringutil // Package stringutil contains utility functions for working with strings. // Reverse returns its argument string reversed rune-wise left to right. func Reverse(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r) } |
ข้อสังเกตุ
- ชื่อ package จะต้องตรงกับชื่อ directory ที่เก็บไฟล์
- สำหรับ Library นั้น ห้ามมีชื่อ package “main” ในไฟล์ใดๆก็ตาม ไม่อย่างนั้นจะฟ้อง error เวลา import จาก package อื่น ลองแก้ code และทดสอบดู
- ชื่อไฟล์ ไม่เกี่ยวข้องใดๆกับ package หรือ function จะตั้งชื่อไฟล์เป็นอะไรก็ได้
Program
main.go
1 2 3 4 5 6 7 8 9 10 | package main import ( "fmt" "github.com/shall-we-go/stringutil" ) func main() { fmt.Println(stringutil.Reverse("dlroW olleH")) } |
ข้อสังเกตุ
- ชื่อ package คือ main และไม่จำเป็นต้องตรงกับชื่อ directory ที่เก็บไฟล์ แต่ในการใช้คำสั่ง
$ go run
จะมองหา package “main” เท่านั้น แล้วถ้าไม่ใช่ main package ล่ะ? ลองแก้ code และทดสอบดู - ชื่อ package คือ main และไม่จำเป็นต้องตรงกับชื่อ directory ที่เก็บไฟล์ แต่ในการใช้คำสั่ง
$ go run
จะมองหา package main เท่านั้น แล้วถ้าไม่ใช่ main package ล่ะ? ลองแก้ code และทดสอบดู - มีการ import library “github.com/shall-we-go/stringutil” ซึ่งเป็น path ตั้งแต่ $GOPATH/src/
- ในการใช้คำสั่ง
$ go run github.com/shall-we-go/reverse-hello-world
เป็นการเรียก main() ในไฟล์ใดๆใน package ที่ระบุ แล้วถ้ามี main() มากกว่า 1 ไฟล์ล่ะ? ลองแก้ code และทดสอบดู
แล้วถ้า Library นี้ไม่ได้เป็น Local library ล่ะ? ให้ลองทดสอบดังนี้
- ลบ directory stringutil
- ใช้คำสั่ง
$ go run github.com/shall-we-go/reverse-hello-world
จะเจอ error ประมาณนี้main.go:6:2: cannot find package "github.com/shall-we-go/stringutil" in any of: /usr/local/Cellar/go/1.11/libexec/src/github.com/shall-we-go/stringutil (from $GOROOT) /d/go/src/github.com/shall-we-go/stringutil (from $GOPATH)
- ใช้คำสั่ง
$ go get github.com/shall-we-go/stringutil
หรือ$ go get -v ./...
ที่ directory $GOPATH/src/ เพื่อ download library จาก GitHub (มีอีกวิธีคือการใช้go mod
ซึ่งเนื้อหาจะอยู่ในตอนต่อๆไป) - ลองทดสอบโปรแกรมอีกครั้งก็จะใช้งานได้ปกติละ
ติดตามต่อตอนหน้า มาดูเรื่อง Testing กัน