PY
GO

Python to Go

10 lessons

Progress0%
1Variables & Types2Functions3Lists → Slices, Dicts → Maps4Classes → Structs5asyncio → Goroutines6Modules → Packages7Interfaces8Error Handling Patterns9Testing10Standard Library
All Mirror Courses
PY
GO
Testing
MirrorLesson 9 of 10
Lesson 9

Testing

Go's built-in testing package vs pytest — table-driven vs parametrize

Introduction

In this lesson, you'll learn about testing in Go. Coming from Python, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
PY
From Python:

In Python, you're familiar with go's built-in testing package vs pytest — table-driven vs parametrize.

GO
In Go:

Go has its own approach to go's built-in testing package vs pytest — table-driven vs parametrize, which we'll explore step by step.

The Go Way

Let's see how Go handles this concept. Here's a typical example:

GO
Go Example
// math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    if got := Add(1, 2); got != 3 {
        t.Errorf("Add(1,2)=%d; want 3", got)
    }
}

// Table-driven (= pytest.parametrize)
func TestDivide(t *testing.T) {
    tests := []struct {
        name    string
        a, b    float64
        want    float64
        wantErr bool
    }{
        {"normal 6/3",    6, 3, 2, false},
        {"zero dividend", 0, 5, 0, false},
        {"divide by zero",1, 0, 0, true},
    }
    for _, tc := range tests {
        t.Run(tc.name, func(t *testing.T) {
            got, err := Divide(tc.a, tc.b)
            if tc.wantErr {
                if err == nil { t.Error("expected error") }
                return
            }
            if err != nil { t.Fatalf("unexpected: %v", err) }
            if got != tc.want {
                t.Errorf("got %v; want %v", got, tc.want)
            }
        })
    }
}

// TestMain for setup/teardown (= pytest fixtures)
func TestMain(m *testing.M) {
    setupDB()
    code := m.Run()
    teardownDB()
    os.Exit(code)
}

// Run: go test ./...
// Verbose: go test -v ./...
// Specific: go test -run TestAdd ./...
// Coverage: go test -cover ./...

Comparing to Python

Here's how you might have written similar code in Python:

PY
Python (What you know)
# test_math.py
import pytest
from math_module import add, divide

def test_add():
    assert add(1, 2) == 3
    assert add(-1, 1) == 0

# Parametrized tests
@pytest.mark.parametrize("a,b,expected", [
    (2, 1, 2),
    (6, 3, 2),
    (0, 5, 0),
])
def test_divide(a, b, expected):
    assert divide(a, b) == expected

# Test exceptions
def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError):
        divide(1, 0)

# Fixtures
@pytest.fixture
def db():
    conn = connect_db()
    yield conn
    conn.close()

def test_query(db):
    result = db.query("SELECT 1")
    assert result == 1

# Run: pytest
# Run verbose: pytest -v
# Run specific: pytest test_math.py::test_add
Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

GO
In Go:

Go test files end in _test.go; test functions are TestXxx(t *testing.T)

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

GO
In Go:

Table-driven tests (slice of structs + range) replaces pytest.parametrize

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

GO
In Go:

TestMain(m *testing.M) replaces pytest session-level fixtures

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

GO
In Go:

t.Error continues the test; t.Fatal stops — unlike pytest which always stops on assert

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

GO
In Go:

No test runner needed — 'go test' is part of the Go toolchain

Step-by-Step Breakdown

1. Test File Conventions

Test files end in _test.go. They can be in the same package (white-box) or package_test (black-box). No framework needed.

PY
Python
# test_math.py — any file starting with test_
GO
Go
// math_test.go — must end in _test.go
package math  // or: package math_test

import "testing"

func TestAdd(t *testing.T) { ... }

2. Table-Driven Tests

Go's idiom for parametrized tests. Define test cases in a struct slice, loop with t.Run() for sub-tests.

PY
Python
@pytest.mark.parametrize("a,b,c", [(1,2,3),(4,5,9)])
def test_add(a, b, c): assert add(a,b) == c
GO
Go
tests := []struct{a,b,want int}{
    {1,2,3},{4,5,9},
}
for _, tc := range tests {
    t.Run(fmt.Sprintf("%d+%d",tc.a,tc.b), func(t *testing.T){
        if got:=Add(tc.a,tc.b); got!=tc.want {
            t.Errorf("got %d; want %d",got,tc.want)
        }
    })
}

3. Error vs Fatal

t.Error marks failure and continues; t.Fatal stops the test. Use Fatal when subsequent checks would be meaningless.

PY
Python
assert result is not None    # pytest stops here
assert result.name == "Alice"
GO
Go
if result == nil { t.Fatal("result is nil") } // stop
if result.Name != "Alice" { t.Errorf(...) }   // continue

4. Benchmarks

Benchmark functions measure performance. The testing framework sets b.N to get statistically significant results.

PY
Python
# No pytest built-in; use pytest-benchmark plugin
GO
Go
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(1, 2)
    }
}
// go test -bench=BenchmarkAdd -benchmem
Rule of Thumb
Call b.ResetTimer() after expensive setup to exclude setup time from benchmark measurements.

Common Mistakes

When coming from Python, developers often make these mistakes:

  • Go test files end in _test.go; test functions are TestXxx(t *testing.T)
  • Table-driven tests (slice of structs + range) replaces pytest.parametrize
  • TestMain(m *testing.M) replaces pytest session-level fixtures
Common Pitfall
Don't assume Go works exactly like Python. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • _test.go files; TestXxx(t *testing.T) — no framework, no assertions library needed
  • Table-driven tests: struct slice + t.Run() = pytest.parametrize
  • t.Error continues; t.Fatal stops — choose based on whether subsequent checks are meaningful
  • go test -cover for coverage; -bench for benchmarks; -run=Regexp for filtering
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your Python code in Go to practice these concepts.
PreviousNext