Testing in Go: table-driven tests and standard-library-first workflows
Translate Rust test habits into Go's testing package and table-driven style.
by the end of this lesson you can
- →Uses testing.T clearly
- →Chooses a table-driven loop for related cases
- →Uses explicit failure messages instead of macro-style assertions
Overview
Rust developers already know explicit tests with built-in tooling. Go testing feels similarly plain, but the dominant idioms are different: _test.go files, testing.T, and case tables instead of test modules and assertion macros.
In Rust, you often
write #[test] functions near the code and use assertion macros to express expected behavior.
In Go, the common pattern is
to use the standard testing package, plain failure messages, and table-driven tests when many related cases share one shape.
why this difference matters
This lesson should help Rust developers feel productive quickly without importing heavier testing patterns too early.
Rust
#[test]
fn square_returns_16() {
assert_eq!(square(4), 16);
}Go
func TestSquare(t *testing.T) {
got := square(4)
want := 16
if got != want {
t.Fatalf("got %d, want %d", got, want)
}
}Deeper comparison
Rust version
#[test]
fn slugify_cases() {
let cases = [("Hello World", "hello-world")];
for (input, expected) in cases {
assert_eq!(slugify(input), expected);
}
}Go version
func TestSlugify(t *testing.T) {
cases := []struct {
in string
want string
}{{"Hello World", "hello-world"}}
for _, tc := range cases {
if got := slugify(tc.in); got != tc.want {
t.Fatalf("got %q, want %q", got, tc.want)
}
}
}Reflect
Why do table-driven tests feel natural in Go even though they are less macro-driven than Rust tests?
what a strong answer notices
A strong answer mentions scannability, low ceremony, and easy grouping of related inputs and expected outputs.
Rewrite
Rewrite this Rust test into Go using the standard testing package.
Rewrite this Rust
#[test]
fn double_cases() {
for (input, expected) in [(2, 4), (3, 6)] {
assert_eq!(double(input), expected);
}
}what good looks like
- Uses testing.T clearly
- Chooses a table-driven loop for related cases
- Uses explicit failure messages instead of macro-style assertions
Practice
Design a table-driven Go test for a username normalizer with valid and invalid inputs.
success criteria
- Uses a clear case table
- Keeps the loop easy to scan
- Uses failure messages that make mismatches obvious
Common mistakes
- Expecting Rust-style assertion ergonomics before learning Go's standard shape.
- Writing many near-duplicate tests instead of a simple case table.
- Adding third-party test helpers before the standard package is comfortable.
takeaways
- ●This lesson should help Rust developers feel productive quickly without importing heavier testing patterns too early.
- ●A strong answer mentions scannability, low ceremony, and easy grouping of related inputs and expected outputs.
- ●Uses a clear case table