Testing in Go: from pytest fluency to table-driven habits
Translate pytest instincts into Go's standard testing package, table-driven tests, and subtests.
by the end of this lesson you can
- →Uses a slice of test cases
- →Names subtests clearly
- →Compares actual and expected values without relying on assertion magic
Overview
Python developers often arrive with strong pytest habits: compact assertions, fixtures, and a flexible testing style. Go gives you a smaller standard toolbox and expects more explicit structure in return.
In Python, you often
lean on pytest discovery, rich assertion introspection, and fixtures that make test setup feel lightweight and expressive.
In Go, the common pattern is
to use the standard testing package, write table-driven cases for related scenarios, and make inputs and expected outputs visible right in the test body.
why this difference matters
Testing is one of the fastest ways to feel at home in a new language. Once Go's table-driven style clicks, the tests feel plain rather than primitive.
Python
def test_slugify():
assert slugify("Hello World") == "hello-world"Go
func TestSlugify(t *testing.T) {
got := slugify("Hello World")
want := "hello-world"
if got != want {
t.Fatalf("got %q, want %q", got, want)
}
}Deeper comparison
Pytest style
import pytest
@pytest.mark.parametrize(
"value,expected",
[("Go Lang", "go-lang"), ("Test Case", "test-case")],
)
def test_slugify(value, expected):
assert slugify(value) == expectedGo table-driven style
func TestSlugify(t *testing.T) {
tests := []struct {
name string
value string
expected string
}{
{name: "go lang", value: "Go Lang", expected: "go-lang"},
{name: "test case", value: "Test Case", expected: "test-case"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := slugify(tt.value)
if got != tt.expected {
t.Fatalf("got %q, want %q", got, tt.expected)
}
})
}
}Reflect
What does Go gain by making test cases more explicit, even when pytest feels shorter and more ergonomic?
what a strong answer notices
A strong answer mentions visibility of inputs, consistency with standard tooling, and easier scanning of scenarios in table-driven tests.
Rewrite
Rewrite this pytest-style parametrized test into Go using a table-driven test and subtests.
Rewrite this Python
@pytest.mark.parametrize("n,expected", [(2, 4), (3, 9), (4, 16)])
def test_square(n, expected):
assert square(n) == expectedwhat good looks like
- Uses a slice of test cases
- Names subtests clearly
- Compares actual and expected values without relying on assertion magic
Practice
Design a Go test for a function that validates usernames. Include success and failure cases, and explain whether you would use a table-driven pattern.
success criteria
- Covers both valid and invalid inputs
- Makes the test cases easy to scan
- Uses failure messages that explain what went wrong without pytest-style introspection
Common mistakes
- Looking for pytest-style fixtures and magic assertions before learning the standard testing shape.
- Writing one-off tests repeatedly instead of recognizing when a table-driven structure is the clearer fit.
- Expecting third-party testing helpers before getting comfortable with the standard library.
takeaways
- ●Testing is one of the fastest ways to feel at home in a new language. Once Go's table-driven style clicks, the tests feel plain rather than primitive.
- ●A strong answer mentions visibility of inputs, consistency with standard tooling, and easier scanning of scenarios in table-driven tests.
- ●Covers both valid and invalid inputs