Go has built-in testing support via the testing package. No external framework needed -> just write test functions with signature func TestXxx(t *testing.T) and go test automatically finds and runs them. This encourages writing tests alongside code.
Go có hỗ trợ kiểm thử tích hợp sẵn thông qua gói testing. Không cần framework bên ngoài — chỉ viết hàm test với signature func TestXxx(t *testing.T) và go test sẽ tự động tìm và chạy chúng. Điều này khuyến khích viết test cùng với code.
Test functions must start with Test (capitalized), take *testing.T, and live in *_test.go files. Use t.Error, t.Errorf, t.Fatal to report failures. t.Fatal stops the test immediately; t.Error continues.
Hàm test phải bắt đầu bằng Test (viết hoa), nhận *testing.T, và nằm trong file *_test.go. Sử dụng t.Error, t.Errorf, t.Fatal để báo cáo thất bại. t.Fatal dừng test ngay lập tức; t.Error tiếp tục.
1// math.go2package math34func Add(a, b int) int {5 return a + b6}78func Divide(a, b float64) (float64, error) {9 if b == 0 {10 return 0, fmt.Errorf("division by zero")11 }12 return a / b, nilHàm test không được in kết quả — dùng t.Log và t.Logf để debug nếu cần. Chỉ dùng t.Error/t.Fatal để báo cáo kết quả test.
Table-driven tests are idiomatic in Go. Create a slice of structs containing input, expected output, and description. Loop through each test case with t.Run() to create a subtest. This allows testing many inputs without repeating test code.
Mẫu table-driven test là idiomatic trong Go. Tạo một slice của struct chứa input, expected output, và mô tả. Lặp qua từng test case với t.Run() để tạo subtest cho mỗi case. Điều này cho phép kiểm tra nhiều input mà không lặp lại code test.
1package math23import "testing"45func TestAddTableDriven(t *testing.T) {6 tests := []struct {7 name string8 a, b int9 expected int10 }{11 {"positive numbers", 2, 3, 5},12 {"negative numbers", -1, -2, -3},t.Run(name, func) creates a subtest. Subtests are useful because: (1) you can run specific subtests with go test -run TestName/SubtestName, (2) failures report the subtest name clearly, (3) you can split complex test logic into smaller pieces.
t.Run(name, func) tạo một subtest. Subtests có lợi vì: (1) bạn có thể chạy subtest cụ thể với go test -run TestName/SubtestName, (2) các lỗi báo cáo tên subtest rõ ràng, (3) bạn có thể chia tách logic test phức tạp thành các phần nhỏ hơn.
1package math23import "testing"45func TestAddDetailed(t *testing.T) {6 t.Run("positive numbers", func(t *testing.T) {7 if Add(2, 3) != 5 {8 t.Error("failed")9 }10 })1112 t.Run("negative numbers", func(t *testing.T) {Benchmark functions have signature func BenchmarkXxx(b *testing.B), live in *_test.go files. Loop over b.N (iterations determined by the framework) and run the code you want to benchmark. go test -bench=. runs benchmarks. -benchmem shows allocations.
Hàm benchmark có signature func BenchmarkXxx(b *testing.B), nằm trong *_test.go file. Lặp qua b.N (số lần chạy được quyết định bởi framework) và chạy code bạn muốn benchmark. go test -bench=. chạy các benchmark. -benchmem hiển thị cấp phát bộ nhớ.
1package math23import "testing"45func BenchmarkAdd(b *testing.B) {6 for i := 0; i < b.N; i++ {7 Add(2, 3)8 }9}1011func BenchmarkDivide(b *testing.B) {12 for i := 0; i < b.N; i++ {13 Divide(10, 2)14 }15}1617// Run with: go test -bench=. -benchmem18// Output:19// BenchmarkAdd-8 1000000000 1.234 ns/op 0 B/op 0 allocs/op20// BenchmarkDivide-8 100000000 10.56 ns/op 0 B/op 0 allocs/opt.Helper() marks a function as a helper. This makes stack traces point to the test that called the helper, not the helper itself. Useful when you have common setup logic or assertions.
t.Helper() đánh dấu hàm là helper function. Điều này giúp stack trace chỉ vào dòng test mà gọi helper, không phải helper function. Hữu ích khi bạn có chung setup logic hoặc assertion.
1package math23import "testing"45// Helper function for comparing results6func assertEqual(t *testing.T, got, want int, msg string) {7 t.Helper() // Mark as helper so errors point to caller8 if got != want {9 t.Errorf("%s: got %d, want %d", msg, got, want)10 }11}1213func TestAddWithHelper(t *testing.T) {14 assertEqual(t, Add(2, 3), 5, "Add(2, 3)")15 assertEqual(t, Add(-1, 1), 0, "Add(-1, 1)")16 assertEqual(t, Add(0, 0), 0, "Add(0, 0)")17}1819// Error stack trace points to assertEqual call in test,20// not to the comparison inside assertEqualCommon flags: -v (verbose output), -run pattern (run tests matching pattern), -race (detect data races), -cover (code coverage %), -coverprofile file (save coverage data).
Các flag phổ biến: -v (verbose output), -run pattern (chạy test khớp pattern), -race (detect data races), -cover (code coverage %), -coverprofile file (lưu coverage data).
1// Running tests2go test ./... // test all packages3go test -v ./... // verbose output4go test -run TestAdd // run TestAdd* tests5go test -run TestAdd/positive // run TestAdd/positive subtest6go test -race ./... // detect data races78// Coverage9go test -cover ./... // show coverage percentage10go test -coverprofile=coverage.out ./...11go tool cover -html=coverage.out // open HTML coverage report1213// Benchmark14go test -bench=. // run all benchmarks15go test -bench=. -benchmem // show memory allocations16go test -bench=. -benchtime=10s // run for 10 secondsExample functions have signature func ExampleXxx() and are run as tests. Expected output goes in // Output: comment. If no output, the example still runs but is not checked. Examples serve as both tests and documentation.
Hàm example có signature func ExampleXxx() và được chạy như test. Output expected được viết trong comment // Output:. Nếu không có output, example vẫn chạy nhưng không kiểm tra kết quả. Examples phục vụ như test và documentation.
1package math23// ExampleAdd demonstrates the Add function4func ExampleAdd() {5 result := Add(2, 3)6 fmt.Println(result)7 // Output: 58}910// ExampleAdd_negative shows Add with negative numbers11func ExampleAdd_negative() {12 result := Add(-1, -2)13 fmt.Println(result)14 // Output: -315}1617// Examples appear in godoc and can be run with 'go test'18// go test -run Examplefunc TestMain(m *testing.M) lets you run setup before and teardown after all tests. Call m.Run() to run tests, then exit with the status code it returns.
func TestMain(m *testing.M) cho phép bạn chạy code setup trước và teardown sau tất cả test. Gọi m.Run() để chạy các test, rồi exit với status code được trả về.
1package math23import (4 "fmt"5 "testing"6)78func TestMain(m *testing.M) {9 // Setup: runs before all tests10 fmt.Println("Setting up test environment...")11 setupDatabase()12Key Takeaways
Điểm Chính
- func TestXxx(t *testing.T) is the signature for test functionsfunc TestXxx(t *testing.T) là signature cho hàm test
- Table-driven tests are idiomatic Go for testing multiple inputsTable-driven test là idiomatic Go để kiểm tra nhiều input
- t.Run creates subtests that can be run individually with -run flagt.Run tạo subtest có thể chạy riêng lẻ bằng flag -run
- go test -bench=. -benchmem runs performance tests and shows memory allocationsgo test -bench=. -benchmem chạy test hiệu suất và hiển thị cấp phát bộ nhớ
Practice
Test your understanding of this chapter
What does t.Fatal() do differently from t.Error()?
t.Fatal() khác với t.Error() như thế nào?
When using table-driven tests with t.Run(), what is the main benefit?
Khi sử dụng table-driven test với t.Run(), lợi ích chính là gì?
Benchmark functions must start with Benchmark (capitalized) and take *testing.B as a parameter.
Hàm benchmark phải bắt đầu bằng Benchmark (viết hoa) và nhận *testing.B làm tham số.
ExampleAdd() functions are only for documentation; they do not run as tests.
Các hàm ExampleAdd() chỉ dành cho tài liệu; chúng không chạy như test.
Fill in the t.Run() call to create a subtest
Điền vào lệnh gọi t.Run() để tạo subtest
t.("addition", func(t *testing.T) { if Add(2, 3) != 5 { t.Error("failed") } })