By Brian Muramatsu
2019-04-14
Table-driven tests are a type of test where "[e]ach table entry is a complete test case with inputs and expected results, and sometimes with additional information such as a test name to make the test output easily readable." This type of test "...simply iterates through all table entries and for each entry performs the necessary tests... [so, the] test code is written once and amortized over all table entries".
Since it is easy to add table entries, I find myself giggling with glee as I add a new test case. However, I can't help but groan everytime I have to write that anonymous struct of fields and format-specifier-ladden run loop. So, I've included a couple of templates here that I can copy and paste into my own code to eliminate that painful initial setup.
These test templates try to follow best practices:
What was sacrificed to make these templates copy and pastable:
import ( "testing" "github.com/google/go-cmp/cmp" ) func TestMethodName(t *testing.T) { for _, tt := range []struct { desc string input struct{} // Replace with input type. want struct{} // Replace with want type. wantErr bool }{ { input: struct{}{}, // Specify input. want: struct{}{}, // Specify want. wantErr: true, }, } { t.Run(tt.desc, func(t *testing.T) { got, gotErr := func(struct{}) (struct{}, error) { /* Replace with function under test. */ return struct{}{}, nil }(tt.input) if diff := cmp.Diff(tt.want, got); diff != "" { t.Errorf("diff (-want, +got)\n%s", diff) } if (gotErr != nil) != tt.wantErr { t.Errorf("got error: %v, wanted err: %t", gotErr, tt.wantErr) } }) } }
Example:
func TestModelOneDayChart(t *testing.T) { for _, tt := range []struct { desc string input *iex.Stock want *model.Chart wantErr bool }{ { input: &iex.Stock{ Quote: &iex.Quote{CompanyName: "Apple Inc."}, Chart: []*iex.ChartPoint{ { Date: time.Date(2018, time.September, 18, 15, 57, 0, 0, time.UTC), Open: 218.44, High: 218.49, Low: 218.37, Close: 218.49, Volume: 2607, }, }, }, want: &model.Chart{ Quote: &model.Quote{CompanyName: "Apple Inc."}, Range: model.OneDay, TradingSessionSeries: &model.TradingSessionSeries{ TradingSessions: []*model.TradingSession{ { Date: time.Date(2018, time.September, 18, 15, 57, 0, 0, time.UTC), Open: 218.44, High: 218.49, Low: 218.37, Close: 218.49, Volume: 2607, }, }, }, }, }, } { t.Run(tt.desc, func(t *testing.T) { got, gotErr := modelOneDayChart(tt.input) if diff := cmp.Diff(tt.want, got); diff != "" { t.Errorf("diff (-want, +got)\n%s", diff) } if (gotErr != nil) != tt.wantErr { t.Errorf("got error: %v, wanted err: %t", gotErr, tt.wantErr) } }) } }
import ( "testing" "github.com/google/go-cmp/cmp" ) func TestMethodName(t *testing.T) { for _, tt := range []struct { desc string input struct{} // Replace with input type. wantErr bool }{ { input: struct{}{}, // Specify input. wantErr: true, }, } { t.Run(tt.desc, func(t *testing.T) { gotErr := func(struct{}) error { /* Replace with function under test. */ return nil }(tt.input) if (gotErr != nil) != tt.wantErr { t.Errorf("got error: %v, wanted err: %t", gotErr, tt.wantErr) } }) } }
Example:
func TestValidateSymbol(t *testing.T) { for _, tt := range []struct { desc string input string wantErr bool }{ { desc: "valid three letter symbol", input: "SPY", }, { desc: "valid four letter symbol", input: "QQQQ", }, { desc: "lowercase not allowed", input: "spy", wantErr: true, }, { desc: "spaces not allowed", input: "S P Y", wantErr: true, }, { desc: "too long", input: "SPYSPY", wantErr: true, }, { desc: "empty string not allowed", input: "", wantErr: true, }, } { t.Run(tt.desc, func(t *testing.T) { gotErr := ValidateSymbol(tt.input) if (gotErr != nil) != tt.wantErr { t.Errorf("got error: %v, wanted err: %t", gotErr, tt.wantErr) } }) } }
Donate to keep development chugging (0 commits) or just star the repository like 0 others!
bitcoin:38vo2oWYmqBUXCxL3avpueye6dPRahX7gC
© 2020 Brian Muramatsu