Advanced Go Techniques: Embracing TDD and the Art of Refactoring
The article walks through a disciplined Go development workflow—red‑green‑refactor—explaining how test‑driven design, table‑driven tests, minimal viable code, and Go‑specific tooling together produce robust, maintainable code while illustrating each step with concrete examples and code snippets.
1. TDD as test‑driven design
Define desired output behavior first, then write table‑driven tests for the ListItems function.
func TestListItems(t *testing.T) {
cases := []struct {
name string
items []string
want string
}{
{"empty", []string{}, ""},
{"one", []string{"a key"}, "You can see a key here."},
{"two", []string{"a key", "a battery"}, "You can see here a key and a battery."},
{"many", []string{"a", "b", "c"}, "You can see here a, b, and c."},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := ListItems(tc.items)
if got != tc.want {
t.Errorf("got %q, want %q", got, tc.want)
}
})
}
}Test cases serve as living documentation of a function’s semantic contract.
2. Red tests indicate uncovered contract, not bugs
A panic in the initial implementation shows the condition len(items) < 3 is not atomic; it must be split into separate cases for 0, 1, and 2 items.
// initial buggy implementation
if len(items) < 3 {
return result + items[0] + " and " + items[1] + "." // panic when len==1
}Refactored minimal‑viable implementation:
func ListItems(items []string) string {
if len(items) == 0 {
return ""
}
if len(items) == 1 {
return "You can see " + items[0] + " here."
}
if len(items) == 2 {
return "You can see here " + items[0] + " and " + items[1] + "."
}
// default: 3+ items
last := items[len(items)-1]
others := strings.Join(items[:len(items)-1], ", ")
return "You can see here " + others + ", and " + last + "."
}When tests are fully covered, refactoring is zero‑risk.
3. Refactoring for verifiability
Replace chained if with a switch on the length to make mutually exclusive branches explicit.
func ListItems(items []string) string {
n := len(items)
switch n {
case 0:
return ""
case 1:
return fmt.Sprintf("You can see %s here.", items[0])
case 2:
return fmt.Sprintf("You can see here %s and %s.", items[0], items[1])
default:
last := items[n-1]
others := strings.Join(items[:n-1], ", ")
return fmt.Sprintf("You can see here %s, and %s.", others, last)
}
}When complexity grows, extract a helper type:
type ItemLister struct {
conjunction string // e.g., "and", "or"
}
func (l *ItemLister) List(items []string) string {
// same switch logic using l.conjunction
}Only with complete test coverage can refactoring be performed safely.
4. Go‑specific testing practices
Run a single sub‑test quickly: go test -v -run TestListItems/two or use go test --watch with a tool such as gow.
Use table‑driven tests together with github.com/google/go-cmp/cmp for precise diff output:
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("mismatch (-want +got):
%s", diff)
}5. Testing pyramid for trustworthy Go services
Unit tests : testing, cmp, testify/mock – fast verification of core logic.
Integration tests : testing with real DB/API sandbox – validate module interaction.
End‑to‑end tests : testcontainers-go, chromedp – simulate real user paths.
Fuzz testing : go test -fuzz – discover boundary crashes.
Benchmark tests : go test -bench – quantify performance and guard against regressions.
Iterating through red‑green‑refactor, writing minimal viable code, and leveraging Go’s testing ecosystem enables robust, evolvable services.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Go Development Architecture Practice
Daily sharing of Golang-related technical articles, practical resources, language news, tutorials, real-world projects, and more. Looking forward to growing together. Let's go!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
