Chapter 03: Fields Struct for Inputs¶
Description¶
When a constructor or method takes many parameters, inline all of them in the test case table makes rows wide and hard to read. The solution: define a fields struct type in the test that groups related inputs. Each test case has a fields field, and the test body unpacks it into the SUT constructor.
hexago/pkg/version/version_test.go:141—TestVersionStringusestype fields struct { Version, Commit, BuildDate string }
Code¶
package fields_struct_inputs
import "fmt"
type Product struct {
Category string
Code string
Currency string
Description string
Name string
UnitPrice float64
}
func (p Product) String() string {
return fmt.Sprintf("%s | %s | %s | %.2f %s",
p.Code, p.Name, p.Category, p.UnitPrice, p.Currency)
}
Test¶
func TestProduct_String(t *testing.T) {
type fields struct {
Code string
Name string
Category string
Description string
UnitPrice float64
Currency string
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "full product",
fields: fields{
Code: "P001", Name: "Wireless Mouse", Category: "Electronics",
Description: "Ergonomic wireless mouse", UnitPrice: 29.99, Currency: "USD",
},
want: "P001 | Wireless Mouse | Electronics | 29.99 USD",
},
{
name: "minimal product",
fields: fields{
Code: "P002", Name: "Notebook", UnitPrice: 3.50, Currency: "EUR",
},
want: "P002 | Notebook | | 3.50 EUR",
},
{
name: "zero value",
fields: fields{},
want: " | | | 0.00 ",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := Product{
Code: tt.fields.Code,
Name: tt.fields.Name,
Category: tt.fields.Category,
Description: tt.fields.Description,
UnitPrice: tt.fields.UnitPrice,
Currency: tt.fields.Currency,
}
if got := p.String(); got != tt.want {
t.Errorf("Product.String() = %v, want %v", got, tt.want)
}
})
}
}
Testing Approach¶
The fields struct pattern solves a readability problem:
- Named grouping — the
fieldstype gives a name to the input parameter group. When the same struct appears in multiple test functions, it communicates "these inputs belong together." - Compact tables — without the
fieldsstruct, the test case would need 6 inline fields forCode,Name,Category,Description,UnitPrice,Currency. Thefieldsstruct wraps them into one column, keeping the table at 3-4 columns. - Partial initialization — Go's zero-value initialization means you only set the fields you care about. The "minimal product" case only sets 4 fields; the rest get zero values.
- No external dependency — the
fieldstype is declared inside the test file (often inside the test function for single-use cases). It's not exported and doesn't pollute the production API.
View source code on GitHub