Go Fundamentals & Tooling
🧪 Section 1 – Basic Functions & Toolchain
✏️ Test 01 – Go Toolchain Verification
- Run
go versionand verify output is a Go version. - Run
go envand check that Go environment variables are present. - Verify
go mod init …creates a validgo.modfile.
🧪 Section 2 – First Program
✏️ Test 02 – Hello World
Create a program:
package main
import "fmt"
func main() {
fmt.Println("Welcome to Go Programming")
}
Run with:
go run .
Expect: “Welcome to Go Programming”.
🧪 Section 3 – Variables & Types
✏️ Test 03 – Type Declarations
Write a program declaring:
var age intvar height float64var isActive boolvar name string
Print types using %T. Expect: Correct type names printed.
✏️ Test 04 – Type Inference
Use := shorthand inside a function. Expect: Types inferred and code compiles.
✏️ Test 05 – Zero Values
Declare variables without initialization and print them. Expect: Zero values: 0, "", false, etc.
🧪 Section 4 – Control Flow
✏️ Test 06 – If / Else Logic
Write a program with an if/else to classify a number (>0, <0, =0). Expect: Correct branch output.
✏️ Test 07 – Looping
Write three loops:
- Classic
for i := 1; i <= 10; i++ - While-style
for i < … - Infinite loop with a
break
Expect: Correct numbers printed.
✏️ Test 08 – Switch Case
Use switch with multiple values. Expect: Correct fallthrough behavior not happening by default.
🧪 Section 5 – Functions
✏️ Test 09 – Multiply Function
Implement:
func multiply(a, b int) int
Test that multiply(2,3) == 6.
✏️ Test 10 – Divide With Error
Implement:
func divide(a, b int) (int, error)
Test:
divide(10, 2)→ result5, nil errordivide(10, 0)→ non-nil error
✏️ Test 11 – Ignore Return Values
Try ignoring the result value. Expect: Compiler error if result is unused.
🧪 Section 6 – Error Handling
✏️ Test 12 – Custom Error
Create:
func validateAge(age int) error
Test:
validateAge(20)returns nilvalidateAge(15)returns non-nil error
✏️ Test 13 – Propagate Errors
Wrap error with a second function:
func process(age int) error
Test that invalid age propagates error.
🧪 Section 7 – Formatting & Tooling
✏️ Test 14 – go fmt
Before and after running go fmt, formatting should change and tests still pass.
✏️ Test 15 – Unused Import
Create a file with an unused import:
import "math"
Expect: Compiler refuses to build.
🧪 Section 8 – Testing
✏️ Test 16 – Multiply Unit Test
In main_test.go, test:
func TestMultiply(t *testing.T)
Verify multiply(2,3) == 6.
✏️ Test 17 – Failing Test
Deliberately introduce a wrong implementation (e.g., return a + b). Expect: go test reports failure with expected output vs actual.
✏️ Test 18 – Table-Driven Test
Write a table test covering:
| a | b | expected |
|---|---|---|
| 2 | 3 | 5 |
| 0 | 5 | 5 |
| –1 | 1 | 0 |
Expect: All subtests pass.
🧪 Section 9 – Bonus: CLI Calculator Tests
✏️ Test 19 – Calculator CLI
Build a small calculator that accepts:
./calc 10 + 4
Expect:
- Correct addition, subtraction, multiplication
- Division with error handling
- “Invalid operator” for bad input
👉 This combines parsing, error handling, and control flow.
📌 How to Run Tests
From the root of this chapter folder:
go test ./...
Use:
go test -failfast
for early failures and faster feedback. ([raw.githubusercontent.com][1])
🧠 Testing Tips for Chapter 1
✔ Add proper *_test.go suffix ✔ Use the built-in testing package ✔ Avoid silent panics — tests should fail meaningfully ([packtpub.com][2])
Perfect 👍 Below is a complete, clean, idiomatic main_test.go for Chapter 1 – Basics based on the earlier exercises (multiply, divide, validateAge, process).
You can place this inside:
chapter-1-basics/main_test.go
This assumes your implementation file is main.go (or similar) in the same package.
🧪 main_test.go
package main
import (
"errors"
"testing"
)
1️⃣ Test Multiply (Simple Case)
func TestMultiply(t *testing.T) {
result := multiply(2, 3)
expected := 6
if result != expected {
t.Fatalf("expected %d, got %d", expected, result)
}
}
✔ Clear ✔ No unnecessary logging ✔ Fails fast
2️⃣ Table-Driven Test for Multiply (Professional Pattern)
func TestMultiply_TableDriven(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 6},
{"zero value", 0, 5, 0},
{"negative numbers", -2, 3, -6},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := multiply(tt.a, tt.b)
if result != tt.expected {
t.Fatalf("expected %d, got %d", tt.expected, result)
}
})
}
}
This is how Go projects typically structure tests.
3️⃣ Test Divide – Success Case
func TestDivide_Success(t *testing.T) {
result, err := divide(10, 2)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if result != 5 {
t.Fatalf("expected 5, got %d", result)
}
}
4️⃣ Test Divide – Division by Zero
func TestDivide_ByZero(t *testing.T) {
_, err := divide(10, 0)
if err == nil {
t.Fatal("expected error, got nil")
}
}
If you defined a sentinel error like:
var ErrDivideByZero = errors.New("division by zero")
Then you can do:
if !errors.Is(err, ErrDivideByZero) {
t.Fatalf("expected ErrDivideByZero, got %v", err)
}
5️⃣ Test validateAge
func TestValidateAge(t *testing.T) {
tests := []struct {
name string
age int
expectErr bool
}{
{"valid age", 20, false},
{"invalid age", 15, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateAge(tt.age)
if tt.expectErr && err == nil {
t.Fatal("expected error, got nil")
}
if !tt.expectErr && err != nil {
t.Fatalf("unexpected error: %v", err)
}
})
}
}
6️⃣ Test process (Error Propagation)
Assuming:
func process(age int) error {
if err := validateAge(age); err != nil {
return err
}
return nil
}
Test:
func TestProcess_ErrorPropagation(t *testing.T) {
err := process(15)
if err == nil {
t.Fatal("expected propagated error, got nil")
}
}
7️⃣ Example of Deliberate Failure Test
To demonstrate failing tests:
func TestMultiply_FailureExample(t *testing.T) {
result := multiply(2, 2)
expected := 5 // intentionally wrong
if result == expected {
t.Fatal("this test should fail to demonstrate failure reporting")
}
}
(Use this only for teaching purposes.)
🧪 How to Run
From chapter-1-basics:
go test -v
For race detection:
go test -race
🧠 What This Teaches the Student
- Writing
*_test.gofiles properly - Using table-driven tests
- Testing success and failure paths
- Handling errors properly
- Avoiding string comparison of errors
- Clean failure messages
- Professional Go testing structure