Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Idiomatic Golang Tests

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Idiomatic Golang Tests

Avatar for Hussani Oliveira

Hussani Oliveira

March 21, 2019
Tweet

More Decks by Hussani Oliveira

Other Decks in Programming

Transcript

  1. func TestSumValid(t *testing.T) { got := Sum(2, 2) if got

    != 4 { t.Errorf("Sum() = %v, want %v", got, 4) } } func TestSumInvalid(t *testing.T) { // some test logic here }
  2. $ go test -v ./ === RUN TestSumValid --- PASS:

    TestSumValid (0.00s) === RUN TestSumInvalid --- PASS: TestSumInvalid (0.00s) PASS ok simple_calculator 0.008s
  3. func TestSum(t *testing.T) { t.Run("sum-valid", func(t *testing.T) { got :=

    Sum(2, 2) if got != 4 { t.Errorf("Sum() = %v, want %v", got, 4) } }) t.Run("sum-invalid", func(t *testing.T) { // some test logic here }) }
  4. $ go test -v ./ === RUN TestSum === RUN

    TestSum/sum-valid === RUN TestSum/sum-invalid --- PASS: TestSum (0.00s) --- PASS: TestSum/sum-valid (0.00s) --- PASS: TestSum/sum-invalid (0.00s) ok simple_calculator 0.008s
  5. type args struct { a int b int } type

    testCase struct { name string args args want int } tests := []testCase{ {"sum 2+2", args{1, 2}, 3}, }
  6. for _, tt := range tests { t.Run(tt.name, func(t *testing.T)

    { got := Sum(tt.args.a, tt.args.b) if got != tt.want { t.Errorf("Sum() = %v, want %v", got, tt.want) } }) }
  7. $ go test -v ./ === RUN TestSum === RUN

    TestSum/sum_2+2 --- PASS: TestSum (0.00s) --- PASS: TestSum/sum_2+2 (0.00s) ok simple_calculator 0.008s
  8. TEXT TABLE DRIVEN TESTS ▸ Easy to add new test

    cases ▸ Don’t repeat same logic ▸ Easy to reproduce bugs ▸ Can be used even for one test case
  9. func Fibonacci(n uint) uint { if n <= 1 {

    return n } var n2, n1 = uint(0), uint(1) for i := uint(1); i < n; i++ { n2 = n2 + n1 n1, n2 = n2, n1 } return n1 }
  10. func TestFibonacci(t *testing.T) { tests := []struct { name string

    number uint want uint }{ {"10", 10, 55}, {"50", 50, 12586269025}, {"9999999999", 9999999999, 2225628016866617058}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Fibonacci(tt.number); got != tt.want { t.Errorf("Fibonacci() = %v, want %v", got, tt.want) } }) } }
  11. $ go test -v ./ === RUN TestFibonacci === RUN

    TestFibonacci/10 === RUN TestFibonacci/50 === RUN TestFibonacci/9999999999 --- PASS: TestFibonacci (7.49s) --- PASS: TestFibonacci/10 (0.00s) --- PASS: TestFibonacci/50 (0.00s) --- PASS: TestFibonacci/9999999999 (7.49s) PASS ok simple_calculator 7.502s
  12. func TestFibonacci(t *testing.T) { tests := []struct { name string

    number uint want uint isSlow bool }{ {"10", 10, 55, false}, {"50", 50, 12586269025, false}, {"9999999999", 9999999999, 2225628016866617058, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if *skipSlow && tt.isSlow { t.Skipf("Skiped slow test") } if got := Fibonacci(tt.number); got != tt.want { t.Errorf("Fibonacci() = %v, want %v", got, tt.want) } }) } }
  13. $ go test -v -skipSlow ./ === RUN TestFibonacci ===

    RUN TestFibonacci/10 === RUN TestFibonacci/50 === RUN TestFibonacci/9999999999 --- PASS: TestFibonacci (0.00s) --- PASS: TestFibonacci/10 (0.00s) --- PASS: TestFibonacci/50 (0.00s) --- SKIP: TestFibonacci/9999999999 (0.00s) calculator_test.go:73: Skiped slow test PASS ok simple_calculator 0.008s
  14. type Item struct { Value int } func (item Item)

    PersistRand(n int) { rand.Seed(time.Now().UnixNano()) for i := 0; i < n; i++ { go func() { item.Value = rand.Int() }() } }
  15. $ go test -v ./ === RUN TestItem_PersistRand === RUN

    TestItem_PersistRand/10 --- PASS: TestItem_PersistRand (0.00s) --- PASS: TestItem_PersistRand/10 (0.00s) PASS ok idiomatic-tests-examples/kv 0.010s
  16. $ go test -v -race ./ === RUN TestItem_PersistRand ===

    RUN TestItem_PersistRand/10 ================== WARNING: DATA RACE Write at 0x00c0000920c0 by goroutine 9: idiomatic-tests-examples/kv.Item.PersistRand.func1() /Users/hussani/projects/go-workspace/src/idiomatic-tests- examples/kv/storage.go:17 +0x46 Previous write at 0x00c0000920c0 by goroutine 8: idiomatic-tests-examples/kv.Item.PersistRand.func1()
  17. TEXT FIXTURES ▸ Use to complex string input ▸ Use

    to iterate on system folders ▸ Use to loads configuration , json, binary data, etc
  18. func TestFetchPersist(t *testing.T) { // a table test case for

    _, tc := range testCases { golden := filepath.Join( "test-fixtures", tc.name + ".golden" ) expected, _ := ioutil.ReadFile(golden) if got := doSomething(tc.input); got != expected { t.Errorf("doSomething() result doesn't match golden. got %v", got) } ) }
  19. TEXT GOLDEN FILES ▸ Use to compare complex string output

    ▸ Need to be updated always your output changes
  20. TEXT SUMMARY ▸ Always do tests! They are important ▸

    Test using Table Driven ▸ Use flags to control some test execution ▸ Always execute tests with race flag ▸ Don’t put complex or long strings on your test file ▸ Use Fixtures & Golden files ▸ Happy testing :)