store_test.go in Go
A whole test file: table tests, subtests, a fake, a benchmark and a fuzz target.
package store
import (
"errors"
"fmt"
"strings"
"testing"
)
// ErrBadStars means the value was outside zero to three.
var ErrBadStars = errors.New("stars out of range")
// Saver persists one run. Tests supply their own.
type Saver interface {
Save(lang string, stars int) error
}
// Record validates a run and hands it to the saver.
func Record(s Saver, lang string, stars int) error {
if stars < 0 || stars > 3 {
return fmt.Errorf("record %s: %w", lang, ErrBadStars)
}
return s.Save(strings.ToLower(strings.TrimSpace(lang)), stars)
}
type fakeSaver struct {
calls []string
err error
}
func (f *fakeSaver) Save(lang string, stars int) error {
f.calls = append(f.calls, fmt.Sprintf("%s=%d", lang, stars))
return f.err
}
func TestRecordValidates(t *testing.T) {
cases := []struct {
name string
lang string
stars int
wantErr error
wantCall string
}{
{"clean", " Go ", 3, nil, "go=3"},
{"zero is fine", "rust", 0, nil, "rust=0"},
{"too many", "sql", 9, ErrBadStars, ""},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
fake := &fakeSaver{}
err := Record(fake, c.lang, c.stars)
if !errors.Is(err, c.wantErr) {
t.Fatalf("err = %v, want %v", err, c.wantErr)
}
if c.wantCall == "" {
if len(fake.calls) != 0 {
t.Errorf("saved despite the error: %v", fake.calls)
}
return
}
if len(fake.calls) != 1 || fake.calls[0] != c.wantCall {
t.Errorf("calls = %v, want [%s]", fake.calls, c.wantCall)
}
})
}
}
func TestRecordPropagatesSaverError(t *testing.T) {
boom := errors.New("disk full")
if err := Record(&fakeSaver{err: boom}, "go", 2); !errors.Is(err, boom) {
t.Errorf("err = %v, want %v", err, boom)
}
}
func BenchmarkRecord(b *testing.B) {
fake := &fakeSaver{}
b.ReportAllocs()
for b.Loop() {
fake.calls = fake.calls[:0]
_ = Record(fake, " Go ", 3)
}
}
func FuzzRecord(f *testing.F) {
f.Add("go", 3)
f.Fuzz(func(t *testing.T, lang string, stars int) {
fake := &fakeSaver{}
err := Record(fake, lang, stars)
inRange := stars >= 0 && stars <= 3
if inRange && err != nil {
t.Errorf("Record(%q, %d) = %v, want nil", lang, stars, err)
}
if !inRange && !errors.Is(err, ErrBadStars) {
t.Errorf("Record(%q, %d) = %v, want ErrBadStars", lang, stars, err)
}
})
}
How it works
- The fake implements the one-method interface the code depends on.
- Cases live in a slice, so adding one is a single line.
- Errors are compared with errors.Is, never by message.
Keywords and builtins used here
appenderrorforfuncifintinterfacelenrangereturnstringstructtypevar
The run, in numbers
- Lines
- 100
- Characters to type
- 2177
- Tokens
- 570
- Three-star pace
- 110 tpm
At the three-star pace of 110 tokens a minute, this run takes about 311 seconds.
Step 1 of 1 in Encore, step 13 of 13 in Testing.