Testing Basics with testing package
Go’s testing package provides a simple yet powerful framework for writing tests. Testing is a first-class citizen in Go.
Writing Tests
Basic Test
// math.go
package main
func Add(a, b int) int {
return a + b
}
// math_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
Running Tests
# Run all tests
go test
# Run with verbose output
go test -v
# Run specific test
go test -run TestAdd
# Run with coverage
go test -cover
# Generate coverage report
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
Test Organization
Table-Driven Tests
package main
import "testing"
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -2, -3, -5},
{"mixed numbers", 5, -3, 2},
{"zero", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
})
}
}
Subtests
package main
import "testing"
func TestMath(t *testing.T) {
t.Run("Addition", func(t *testing.T) {
if Add(2, 3) != 5 {
t.Error("Addition failed")
}
})
t.Run("Subtraction", func(t *testing.T) {
if Subtract(5, 3) != 2 {
t.Error("Subtraction failed")
}
})
}
Test Helpers
Helper Functions
package main
import "testing"
func TestWithHelper(t *testing.T) {
t.Helper() // Mark as helper function
// Test code
}
func assertEqual(t *testing.T, got, want interface{}) {
t.Helper()
if got != want {
t.Errorf("got %v; want %v", got, want)
}
}
func TestAdd(t *testing.T) {
assertEqual(t, Add(2, 3), 5)
}
Benchmarking
Basic Benchmark
package main
import "testing"
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
func BenchmarkAddLarge(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1000000, 2000000)
}
}
Running Benchmarks
# Run benchmarks
go test -bench=.
# Run specific benchmark
go test -bench=BenchmarkAdd
# Run with memory stats
go test -bench=. -benchmem
# Run for specific duration
go test -bench=. -benchtime=10s
Test Coverage
Checking Coverage
# Generate coverage profile
go test -coverprofile=coverage.out
# View coverage percentage
go test -cover
# View coverage in HTML
go tool cover -html=coverage.out
Coverage Example
package main
import "testing"
func TestAddCoverage(t *testing.T) {
// Test positive case
if Add(2, 3) != 5 {
t.Error("positive case failed")
}
// Test negative case
if Add(-2, -3) != -5 {
t.Error("negative case failed")
}
// Test zero case
if Add(0, 0) != 0 {
t.Error("zero case failed")
}
}
Best Practices
โ Good: Clear Test Names
// DO: Use descriptive test names
func TestAddPositiveNumbers(t *testing.T) {
// ...
}
func TestAddNegativeNumbers(t *testing.T) {
// ...
}
โ Bad: Vague Test Names
// DON'T: Use unclear names
func TestAdd1(t *testing.T) {
// ...
}
func TestAdd2(t *testing.T) {
// ...
}
โ Good: Test Edge Cases
// DO: Test edge cases
func TestDivide(t *testing.T) {
tests := []struct {
a, b float64
expected float64
}{
{10, 2, 5}, // normal case
{0, 5, 0}, // zero numerator
{-10, 2, -5}, // negative
}
// ...
}
โ Good: Use Table-Driven Tests
// DO: Use table-driven tests for multiple cases
func TestAdd(t *testing.T) {
tests := []struct {
a, b int
expected int
}{
{2, 3, 5},
{-2, -3, -5},
{0, 0, 0},
}
for _, tt := range tests {
t.Run("", func(t *testing.T) {
if Add(tt.a, tt.b) != tt.expected {
t.Error("test failed")
}
})
}
}
Summary
Go’s testing package provides:
- Simple test syntax with
*testing.T - Table-driven tests for multiple cases
- Subtests for organization
- Benchmarking for performance testing
- Coverage analysis for quality metrics
These features make testing a natural part of Go development.
Comments