Structs, Functions & Packages
These tests are written in a clear, idiomatic Go style using table-driven tests, proper assertions, and no unnecessary output. They should guide learners to test their implementations correctly.
📦 Test Setup
Before writing tests, make sure your functions and types are in appropriate packages. Example project layout for chapter:
chapter-2-structs-functions/
│
├── user.go
├── user_test.go
├── mathutil.go
├── mathutil_test.go
├── ...
🧠 Tests for Functions
1️⃣ Test Divide Function
package mathutil
import "testing"
func TestDivide(t *testing.T) {
tests := []struct {
name string
a, b int
want int
wantErr bool
}{
{"normal division", 10, 2, 5, false},
{"division by zero", 10, 0, 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Divide(tt.a, tt.b)
if (err != nil) != tt.wantErr {
t.Errorf("Divide() error = %v, wantErr %v", err, tt.wantErr)
}
if got != tt.want {
t.Errorf("Divide() got = %v, want %v", got, tt.want)
}
})
}
}
🧱 Tests for Structs & Methods
2️⃣ Test Value & Pointer Receiver Behavior
package user
import "testing"
func TestUpdateName(t *testing.T) {
user := User{ID: 1, Name: "Alice"}
user.UpdateName("Bob")
if user.Name != "Bob" {
t.Errorf("expected name to be Bob, got %s", user.Name)
}
}
func TestUpdateEmailPointer(t *testing.T) {
user := &User{ID: 1, Name: "Alice", Email: "a@example.com"}
user.UpdateEmail("bob@example.com")
if user.Email != "bob@example.com" {
t.Errorf("expected email to be updated, got %s", user.Email)
}
}
🧬 Tests for Composition
3️⃣ Embed Struct Behavior
package customer
import (
"testing"
)
func TestCustomerAddressEmbedding(t *testing.T) {
c := Customer{
Name: "Test",
Address: Address{
City: "Bangalore",
Country: "India",
},
}
if c.City != "Bangalore" || c.Country != "India" {
t.Errorf("Expected embedded fields to be accessible directly")
}
}
🧪 Tests for Export Rules
4️⃣ Visibility & Package Export
package helper
import "testing"
func TestExportedFunc(t *testing.T) {
got := ExportedFunc()
if got != "hello" {
t.Errorf("ExportedFunc() = %q, want %q", got, "hello")
}
}
// This test *should* fail to compile if you try to reference an unexported symbol
// Uncomment to verify:
// func TestUnexportedFunc(t *testing.T) {
// _ = unexportedFunc() // should not compile
// }
📂 Tests for Package Structure
5️⃣ Creating Instances from Another Package
package main
import (
"testing"
"yourmodule/user"
)
func TestNewUserInstance(t *testing.T) {
u := user.New("1", "Alice", "a@example.com")
if u.Name != "Alice" {
t.Errorf("expected user name Alice, got %s", u.Name)
}
}
Adjust
"yourmodule/user"to match your actual module path.
📦 Bonus: Table-Driven Test for Multiple Struct Behaviors
package user
import "testing"
func TestMultipleUserUpdates(t *testing.T) {
tests := []struct {
name string
initialName string
updateEmail string
wantEmail string
}{
{"update only email", "Alice", "alice@x.com", "alice@x.com"},
{"no change to email", "Bob", "", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
u := &User{Name: tt.initialName, Email: ""}
u.UpdateEmail(tt.updateEmail)
if u.Email != tt.wantEmail {
t.Errorf("email = %s, want %s", u.Email, tt.wantEmail)
}
})
}
}
✅ Test Best Practices Covered
✔ Table-driven tests ✔ Clear assertion messages ✔ Minimal duplication ✔ Separation of responsibilities ✔ No reliance on global state
🔍 Tips for Learners
- Run tests with
go test ./... - Use
go test -coverto see coverage - Use
go test -failfastto stop on first failing test - Write tests before writing implementation (TDD mindset)