Perfect — let’s elevate the testing quality.
Below is a clean, idiomatic, production-style unit test suite for Week 3 concepts. This reflects:
- Table-driven tests
- Proper error checking (
errors.Is,errors.As) - No string comparison for errors
- Clean test naming
- No unnecessary logging
- Focused assertions
🧪 service.go (Reference Implementation Under Test)
package service
import (
"errors"
"fmt"
)
var ErrStorageFailure = errors.New("storage failure")
type Storage interface {
Save(data string) error
}
type ValidationError struct {
Field string
Message string
}
func (v ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", v.Field, v.Message)
}
type UserService struct {
storage Storage
}
func NewUserService(s Storage) *UserService {
return &UserService{storage: s}
}
func (u *UserService) CreateUser(name string) error {
if name == "" {
return ValidationError{
Field: "name",
Message: "cannot be empty",
}
}
if err := u.storage.Save(name); err != nil {
return fmt.Errorf("failed to create user: %w", err)
}
return nil
}
🧪 service_test.go (Clean, Idiomatic Tests)
package service
import (
"errors"
"testing"
)
🔹 Mock Storage (Clean & Minimal)
type mockStorage struct {
shouldFail bool
}
func (m *mockStorage) Save(data string) error {
if m.shouldFail {
return ErrStorageFailure
}
return nil
}
1️⃣ Test Success Path
func TestCreateUser_Success(t *testing.T) {
storage := &mockStorage{shouldFail: false}
service := NewUserService(storage)
err := service.CreateUser("Aditya")
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
}
Why this is clean:
- No unnecessary checks
- Fails fast
- Clear intent
2️⃣ Test Validation Error (Using errors.As)
func TestCreateUser_ValidationError(t *testing.T) {
storage := &mockStorage{}
service := NewUserService(storage)
err := service.CreateUser("")
if err == nil {
t.Fatal("expected validation error, got nil")
}
var ve ValidationError
if !errors.As(err, &ve) {
t.Fatalf("expected ValidationError, got %T", err)
}
if ve.Field != "name" {
t.Fatalf("expected field 'name', got %s", ve.Field)
}
}
Why this is idiomatic:
- Uses
errors.As - Does not compare strings
- Verifies structured data
3️⃣ Test Wrapped Storage Error (Using errors.Is)
func TestCreateUser_StorageFailure(t *testing.T) {
storage := &mockStorage{shouldFail: true}
service := NewUserService(storage)
err := service.CreateUser("Aditya")
if err == nil {
t.Fatal("expected error, got nil")
}
if !errors.Is(err, ErrStorageFailure) {
t.Fatalf("expected wrapped ErrStorageFailure, got %v", err)
}
}
Why this is correct:
- Uses
errors.Is - Does not rely on error string
- Validates wrapping behavior
4️⃣ Table-Driven Test (Professional Pattern)
func TestCreateUser_TableDriven(t *testing.T) {
tests := []struct {
name string
input string
shouldFail bool
expectError bool
}{
{
name: "valid user",
input: "Aditya",
shouldFail: false,
expectError: false,
},
{
name: "empty name",
input: "",
shouldFail: false,
expectError: true,
},
{
name: "storage failure",
input: "Aditya",
shouldFail: true,
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
storage := &mockStorage{shouldFail: tt.shouldFail}
service := NewUserService(storage)
err := service.CreateUser(tt.input)
if tt.expectError && err == nil {
t.Fatal("expected error, got nil")
}
if !tt.expectError && err != nil {
t.Fatalf("unexpected error: %v", err)
}
})
}
}
Why this is senior-level:
- Scalable
- Clean test names
- No duplicated logic
- Clear scenario separation
🧠 What Makes These Tests “Clean”
✔ Small, focused
Each test verifies one behavior.
✔ No string comparison of errors
We use:
errors.Iserrors.As
✔ No unnecessary logging
Only fail when necessary.
✔ No shared mutable global state
✔ Proper naming convention
TestFunction_Scenario
🎯 What Student Learns From This
- Interface-based mocking
- Structured error testing
- Proper use of
errors.Isanderrors.As - Table-driven test design
- Writing maintainable tests
This is exactly how Go is tested in real-world backend systems.