diff --git a/arrays-and-slices.md b/arrays-and-slices.md index a6cb682..f25eb56 100644 --- a/arrays-and-slices.md +++ b/arrays-and-slices.md @@ -22,14 +22,14 @@ import "testing" func TestSum(t *testing.T) { - numbers := [5]int{1, 2, 3, 4, 5} + numbers := [5]int{1, 2, 3, 4, 5} - got := Sum(numbers) - want := 15 + got := Sum(numbers) + want := 15 - if got != want { - t.Errorf("got %d want %d given, %v", got, want, numbers) - } + if got != want { + t.Errorf("got %d want %d given, %v", got, want, numbers) + } } ``` @@ -57,7 +57,7 @@ In `sum.go` package main func Sum(numbers [5]int) int { - return 0 + return 0 } ``` @@ -69,11 +69,11 @@ Your test should now fail with _a clear error message_ ```go func Sum(numbers [5]int) int { - sum := 0 - for i := 0; i < 5; i++ { - sum += numbers[i] - } - return sum + sum := 0 + for i := 0; i < 5; i++ { + sum += numbers[i] + } + return sum } ``` @@ -87,11 +87,11 @@ Let's introduce [`range`](https://gobyexample.com/range) to help clean up our co ```go func Sum(numbers [5]int) int { - sum := 0 - for _, number := range numbers { - sum += number - } - return sum + sum := 0 + for _, number := range numbers { + sum += number + } + return sum } ``` @@ -125,27 +125,27 @@ declaring them ```go func TestSum(t *testing.T) { - t.Run("collection of 5 numbers", func(t *testing.T) { - numbers := [5]int{1, 2, 3, 4, 5} + t.Run("collection of 5 numbers", func(t *testing.T) { + numbers := [5]int{1, 2, 3, 4, 5} - got := Sum(numbers) - want := 15 + got := Sum(numbers) + want := 15 - if got != want { - t.Errorf("got %d want %d given, %v", got, want, numbers) - } - }) + if got != want { + t.Errorf("got %d want %d given, %v", got, want, numbers) + } + }) - t.Run("collection of any size", func(t *testing.T) { - numbers := []int{1, 2, 3} + t.Run("collection of any size", func(t *testing.T) { + numbers := []int{1, 2, 3} - got := Sum(numbers) - want := 6 + got := Sum(numbers) + want := 6 - if got != want { - t.Errorf("got %d want %d given, %v", got, want, numbers) - } - }) + if got != want { + t.Errorf("got %d want %d given, %v", got, want, numbers) + } + }) } ``` @@ -169,11 +169,11 @@ In our case, no-one else is using our function so rather than having two functio ```go func Sum(numbers []int) int { - sum := 0 - for _, number := range numbers { - sum += number - } - return sum + sum := 0 + for _, number := range numbers { + sum += number + } + return sum } ``` @@ -190,27 +190,27 @@ We had already refactored `Sum` and all we've done is changing from arrays to sl ```go func TestSum(t *testing.T) { - t.Run("collection of 5 numbers", func(t *testing.T) { - numbers := []int{1, 2, 3, 4, 5} + t.Run("collection of 5 numbers", func(t *testing.T) { + numbers := []int{1, 2, 3, 4, 5} - got := Sum(numbers) - want := 15 + got := Sum(numbers) + want := 15 - if got != want { - t.Errorf("got %d want %d given, %v", got, want, numbers) - } - }) + if got != want { + t.Errorf("got %d want %d given, %v", got, want, numbers) + } + }) - t.Run("collection of any size", func(t *testing.T) { - numbers := []int{1, 2, 3} + t.Run("collection of any size", func(t *testing.T) { + numbers := []int{1, 2, 3} - got := Sum(numbers) - want := 6 + got := Sum(numbers) + want := 6 - if got != want { - t.Errorf("got %d want %d given, %v", got, want, numbers) - } - }) + if got != want { + t.Errorf("got %d want %d given, %v", got, want, numbers) + } + }) } ``` @@ -263,12 +263,12 @@ or ```go func TestSumAll(t *testing.T) { - got := SumAll([]int{1,2}, []int{0,9}) - want := []int{3, 9} + got := SumAll([]int{1, 2}, []int{0, 9}) + want := []int{3, 9} - if got != want { - t.Errorf("got %v want %v", got, want) - } + if got != want { + t.Errorf("got %v want %v", got, want) + } } ``` @@ -284,7 +284,7 @@ Go can let you write [_variadic functions_](https://gobyexample.com/variadic-fun ```go func SumAll(numbersToSum ...[]int) (sums []int) { - return + return } ``` @@ -300,12 +300,12 @@ useful for seeing if _any_ two variables are the same. ```go func TestSumAll(t *testing.T) { - got := SumAll([]int{1,2}, []int{0,9}) - want := []int{3, 9} + got := SumAll([]int{1, 2}, []int{0, 9}) + want := []int{3, 9} - if !reflect.DeepEqual(got, want) { - t.Errorf("got %v want %v", got, want) - } + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v want %v", got, want) + } } ``` @@ -318,12 +318,12 @@ temporarily change the test to: ```go func TestSumAll(t *testing.T) { - got := SumAll([]int{1,2}, []int{0,9}) - want := "bob" + got := SumAll([]int{1, 2}, []int{0, 9}) + want := "bob" - if !reflect.DeepEqual(got, want) { - t.Errorf("got %v want %v", got, want) - } + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v want %v", got, want) + } } ``` @@ -343,14 +343,14 @@ What we need to do is iterate over the varargs, calculate the sum using our ```go func SumAll(numbersToSum ...[]int) []int { - lengthOfNumbers := len(numbersToSum) - sums := make([]int, lengthOfNumbers) + lengthOfNumbers := len(numbersToSum) + sums := make([]int, lengthOfNumbers) - for i, numbers := range numbersToSum { - sums[i] = Sum(numbers) - } + for i, numbers := range numbersToSum { + sums[i] = Sum(numbers) + } - return sums + return sums } ``` @@ -374,12 +374,12 @@ returning a new slice with all the items in it. ```go func SumAll(numbersToSum ...[]int) []int { - var sums []int - for _, numbers := range numbersToSum { - sums = append(sums, Sum(numbers)) - } + var sums []int + for _, numbers := range numbersToSum { + sums = append(sums, Sum(numbers)) + } - return sums + return sums } ``` @@ -394,12 +394,12 @@ all the items apart from the first one \(the "head"\) ```go func TestSumAllTails(t *testing.T) { - got := SumAllTails([]int{1,2}, []int{0,9}) - want := []int{2, 9} + got := SumAllTails([]int{1, 2}, []int{0, 9}) + want := []int{2, 9} - if !reflect.DeepEqual(got, want) { - t.Errorf("got %v want %v", got, want) - } + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v want %v", got, want) + } } ``` @@ -417,13 +417,13 @@ Rename the function to `SumAllTails` and re-run the test ```go func SumAllTails(numbersToSum ...[]int) []int { - var sums []int - for _, numbers := range numbersToSum { - tail := numbers[1:] - sums = append(sums, Sum(tail)) - } + var sums []int + for _, numbers := range numbersToSum { + tail := numbers[1:] + sums = append(sums, Sum(tail)) + } - return sums + return sums } ``` @@ -446,23 +446,23 @@ capture all elements from `myEmptySlice[1:]`? ```go func TestSumAllTails(t *testing.T) { - t.Run("make the sums of some slices", func(t *testing.T) { - got := SumAllTails([]int{1,2}, []int{0,9}) - want := []int{2, 9} + t.Run("make the sums of some slices", func(t *testing.T) { + got := SumAllTails([]int{1, 2}, []int{0, 9}) + want := []int{2, 9} - if !reflect.DeepEqual(got, want) { - t.Errorf("got %v want %v", got, want) - } - }) + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v want %v", got, want) + } + }) - t.Run("safely sum empty slices", func(t *testing.T) { - got := SumAllTails([]int{}, []int{3, 4, 5}) - want := []int{0, 9} + t.Run("safely sum empty slices", func(t *testing.T) { + got := SumAllTails([]int{}, []int{3, 4, 5}) + want := []int{0, 9} - if !reflect.DeepEqual(got, want) { - t.Errorf("got %v want %v", got, want) - } - }) + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v want %v", got, want) + } + }) } ``` @@ -482,17 +482,17 @@ works, runtime errors are our enemies because they affect our users. ```go func SumAllTails(numbersToSum ...[]int) []int { - var sums []int - for _, numbers := range numbersToSum { - if len(numbers) == 0 { - sums = append(sums, 0) - } else { - tail := numbers[1:] - sums = append(sums, Sum(tail)) - } - } + var sums []int + for _, numbers := range numbersToSum { + if len(numbers) == 0 { + sums = append(sums, 0) + } else { + tail := numbers[1:] + sums = append(sums, Sum(tail)) + } + } - return sums + return sums } ``` @@ -503,24 +503,24 @@ Our tests have some repeated code around assertion again, let's extract that int ```go func TestSumAllTails(t *testing.T) { - checkSums := func(t *testing.T, got, want []int) { - t.Helper() - if !reflect.DeepEqual(got, want) { - t.Errorf("got %v want %v", got, want) - } - } + checkSums := func(t *testing.T, got, want []int) { + t.Helper() + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v want %v", got, want) + } + } - t.Run("make the sums of tails of", func(t *testing.T) { - got := SumAllTails([]int{1, 2}, []int{0, 9}) - want := []int{2, 9} - checkSums(t, got, want) - }) + t.Run("make the sums of tails of", func(t *testing.T) { + got := SumAllTails([]int{1, 2}, []int{0, 9}) + want := []int{2, 9} + checkSums(t, got, want) + }) - t.Run("safely sum empty slices", func(t *testing.T) { - got := SumAllTails([]int{}, []int{3, 4, 5}) - want := []int{0, 9} - checkSums(t, got, want) - }) + t.Run("safely sum empty slices", func(t *testing.T) { + got := SumAllTails([]int{}, []int{3, 4, 5}) + want := []int{0, 9} + checkSums(t, got, want) + }) } ``` diff --git a/command-line.md b/command-line.md index 02c32d5..e620ffc 100644 --- a/command-line.md +++ b/command-line.md @@ -14,9 +14,9 @@ We have an application with a `main.go` file that launches an HTTP server. The H ```go type PlayerStore interface { - GetPlayerScore(name string) int - RecordWin(name string) - GetLeague() League + GetPlayerScore(name string) int + RecordWin(name string) + GetLeague() League } ``` @@ -71,32 +71,32 @@ The paths will be different on your computer, but it should be similar to this: package main import ( - "log" - "net/http" - "os" - "github.com/quii/learn-go-with-tests/command-line/v1" + "github.com/quii/learn-go-with-tests/command-line/v1" + "log" + "net/http" + "os" ) const dbFileName = "game.db.json" func main() { - db, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666) + db, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666) - if err != nil { - log.Fatalf("problem opening %s %v", dbFileName, err) - } + if err != nil { + log.Fatalf("problem opening %s %v", dbFileName, err) + } - store, err := poker.NewFileSystemPlayerStore(db) + store, err := poker.NewFileSystemPlayerStore(db) - if err != nil { - log.Fatalf("problem creating file system player store, %v ", err) - } + if err != nil { + log.Fatalf("problem creating file system player store, %v ", err) + } - server := poker.NewPlayerServer(store) + server := poker.NewPlayerServer(store) - if err := http.ListenAndServe(":5000", server); err != nil { - log.Fatalf("could not listen on port 5000 %v", err) - } + if err := http.ListenAndServe(":5000", server); err != nil { + log.Fatalf("could not listen on port 5000 %v", err) + } } ``` @@ -122,7 +122,7 @@ package main import "fmt" func main() { - fmt.Println("Let's play poker") + fmt.Println("Let's play poker") } ``` @@ -138,13 +138,13 @@ Inside `CLI_test.go` (in the root of the project, not inside `cmd`) ```go func TestCLI(t *testing.T) { - playerStore := &StubPlayerStore{} - cli := &CLI{playerStore} - cli.PlayPoker() + playerStore := &StubPlayerStore{} + cli := &CLI{playerStore} + cli.PlayPoker() - if len(playerStore.winCalls) !=1 { - t.Fatal("expected a win call but didn't get any") - } + if len(playerStore.winCalls) != 1 { + t.Fatal("expected a win call but didn't get any") + } } ``` @@ -168,7 +168,7 @@ You should end up with code like this ```go type CLI struct { - playerStore PlayerStore + playerStore PlayerStore } func (cli *CLI) PlayPoker() {} @@ -186,7 +186,7 @@ FAIL ```go func (cli *CLI) PlayPoker() { - cli.playerStore.RecordWin("Cleo") + cli.playerStore.RecordWin("Cleo") } ``` @@ -200,22 +200,22 @@ Let's extend our test to exercise this. ```go func TestCLI(t *testing.T) { - in := strings.NewReader("Chris wins\n") - playerStore := &StubPlayerStore{} + in := strings.NewReader("Chris wins\n") + playerStore := &StubPlayerStore{} - cli := &CLI{playerStore, in} - cli.PlayPoker() + cli := &CLI{playerStore, in} + cli.PlayPoker() - if len(playerStore.winCalls) < 1 { - t.Fatal("expected a win call but didn't get any") - } + if len(playerStore.winCalls) < 1 { + t.Fatal("expected a win call but didn't get any") + } - got := playerStore.winCalls[0] - want := "Chris" + got := playerStore.winCalls[0] + want := "Chris" - if got != want { - t.Errorf("didn't record correct winner, got %q, want %q", got, want) - } + if got != want { + t.Errorf("didn't record correct winner, got %q, want %q", got, want) + } } ``` @@ -233,8 +233,8 @@ We need to add our new dependency into `CLI`. ```go type CLI struct { - playerStore PlayerStore - in io.Reader + playerStore PlayerStore + in io.Reader } ``` @@ -250,7 +250,7 @@ Remember to do the strictly easiest thing first ```go func (cli *CLI) PlayPoker() { - cli.playerStore.RecordWin("Chris") + cli.playerStore.RecordWin("Chris") } ``` @@ -262,15 +262,15 @@ In `server_test` we earlier did checks to see if wins are recorded as we have he ```go func assertPlayerWin(t *testing.T, store *StubPlayerStore, winner string) { - t.Helper() + t.Helper() - if len(store.winCalls) != 1 { - t.Fatalf("got %d calls to RecordWin want %d", len(store.winCalls), 1) - } + if len(store.winCalls) != 1 { + t.Fatalf("got %d calls to RecordWin want %d", len(store.winCalls), 1) + } - if store.winCalls[0] != winner { - t.Errorf("did not store correct winner got %q want %q", store.winCalls[0], winner) - } + if store.winCalls[0] != winner { + t.Errorf("did not store correct winner got %q want %q", store.winCalls[0], winner) + } } ``` @@ -280,13 +280,13 @@ The test should now read like so ```go func TestCLI(t *testing.T) { - in := strings.NewReader("Chris wins\n") - playerStore := &StubPlayerStore{} + in := strings.NewReader("Chris wins\n") + playerStore := &StubPlayerStore{} - cli := &CLI{playerStore, in} - cli.PlayPoker() + cli := &CLI{playerStore, in} + cli.PlayPoker() - assertPlayerWin(t, playerStore, "Chris") + assertPlayerWin(t, playerStore, "Chris") } ``` @@ -297,25 +297,25 @@ Now let's write _another_ test with different user input to force us into actual ```go func TestCLI(t *testing.T) { - t.Run("record chris win from user input", func(t *testing.T) { - in := strings.NewReader("Chris wins\n") - playerStore := &StubPlayerStore{} + t.Run("record chris win from user input", func(t *testing.T) { + in := strings.NewReader("Chris wins\n") + playerStore := &StubPlayerStore{} - cli := &CLI{playerStore, in} - cli.PlayPoker() + cli := &CLI{playerStore, in} + cli.PlayPoker() - assertPlayerWin(t, playerStore, "Chris") - }) + assertPlayerWin(t, playerStore, "Chris") + }) - t.Run("record cleo win from user input", func(t *testing.T) { - in := strings.NewReader("Cleo wins\n") - playerStore := &StubPlayerStore{} + t.Run("record cleo win from user input", func(t *testing.T) { + in := strings.NewReader("Cleo wins\n") + playerStore := &StubPlayerStore{} - cli := &CLI{playerStore, in} - cli.PlayPoker() + cli := &CLI{playerStore, in} + cli.PlayPoker() - assertPlayerWin(t, playerStore, "Cleo") - }) + assertPlayerWin(t, playerStore, "Cleo") + }) } ``` @@ -343,18 +343,18 @@ Update the code to the following ```go type CLI struct { - playerStore PlayerStore - in io.Reader + playerStore PlayerStore + in io.Reader } func (cli *CLI) PlayPoker() { reader := bufio.NewScanner(cli.in) reader.Scan() - cli.playerStore.RecordWin(extractWinner(reader.Text())) + cli.playerStore.RecordWin(extractWinner(reader.Text())) } func extractWinner(userInput string) string { - return strings.Replace(userInput, " wins", "", 1) + return strings.Replace(userInput, " wins", "", 1) } ``` @@ -371,32 +371,32 @@ In `main.go` add the following and run it. (you may have to adjust the path of t package main import ( - "fmt" - "github.com/quii/learn-go-with-tests/command-line/v3" - "log" - "os" + "fmt" + "github.com/quii/learn-go-with-tests/command-line/v3" + "log" + "os" ) const dbFileName = "game.db.json" func main() { - fmt.Println("Let's play poker") - fmt.Println("Type {Name} wins to record a win") + fmt.Println("Let's play poker") + fmt.Println("Type {Name} wins to record a win") - db, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666) + db, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666) - if err != nil { - log.Fatalf("problem opening %s %v", dbFileName, err) - } + if err != nil { + log.Fatalf("problem opening %s %v", dbFileName, err) + } - store, err := poker.NewFileSystemPlayerStore(db) + store, err := poker.NewFileSystemPlayerStore(db) - if err != nil { - log.Fatalf("problem creating file system player store, %v ", err) - } + if err != nil { + log.Fatalf("problem creating file system player store, %v ", err) + } - game := poker.CLI{store, os.Stdin} - game.PlayPoker() + game := poker.CLI{store, os.Stdin} + game.PlayPoker() } ``` @@ -454,34 +454,34 @@ package poker import "testing" type StubPlayerStore struct { - scores map[string]int - winCalls []string - league []Player + scores map[string]int + winCalls []string + league []Player } func (s *StubPlayerStore) GetPlayerScore(name string) int { - score := s.scores[name] - return score + score := s.scores[name] + return score } func (s *StubPlayerStore) RecordWin(name string) { - s.winCalls = append(s.winCalls, name) + s.winCalls = append(s.winCalls, name) } func (s *StubPlayerStore) GetLeague() League { - return s.league + return s.league } func AssertPlayerWin(t *testing.T, store *StubPlayerStore, winner string) { - t.Helper() + t.Helper() - if len(store.winCalls) != 1 { - t.Fatalf("got %d calls to RecordWin want %d", len(store.winCalls), 1) - } + if len(store.winCalls) != 1 { + t.Fatalf("got %d calls to RecordWin want %d", len(store.winCalls), 1) + } - if store.winCalls[0] != winner { - t.Errorf("did not store correct winner got %q want %q", store.winCalls[0], winner) - } + if store.winCalls[0] != winner { + t.Errorf("did not store correct winner got %q want %q", store.winCalls[0], winner) + } } // todo for you - the rest of the helpers @@ -494,25 +494,25 @@ In our `CLI` test you'll need to call the code as if you were using it within a ```go func TestCLI(t *testing.T) { - t.Run("record chris win from user input", func(t *testing.T) { - in := strings.NewReader("Chris wins\n") - playerStore := &poker.StubPlayerStore{} + t.Run("record chris win from user input", func(t *testing.T) { + in := strings.NewReader("Chris wins\n") + playerStore := &poker.StubPlayerStore{} - cli := &poker.CLI{playerStore, in} - cli.PlayPoker() + cli := &poker.CLI{playerStore, in} + cli.PlayPoker() - poker.AssertPlayerWin(t, playerStore, "Chris") - }) + poker.AssertPlayerWin(t, playerStore, "Chris") + }) - t.Run("record cleo win from user input", func(t *testing.T) { - in := strings.NewReader("Cleo wins\n") - playerStore := &poker.StubPlayerStore{} + t.Run("record cleo win from user input", func(t *testing.T) { + in := strings.NewReader("Cleo wins\n") + playerStore := &poker.StubPlayerStore{} - cli := &poker.CLI{playerStore, in} - cli.PlayPoker() + cli := &poker.CLI{playerStore, in} + cli.PlayPoker() - poker.AssertPlayerWin(t, playerStore, "Cleo") - }) + poker.AssertPlayerWin(t, playerStore, "Cleo") + }) } ``` @@ -604,25 +604,25 @@ Now refactor both of our applications to use this function to create the store. package main import ( - "github.com/quii/learn-go-with-tests/command-line/v3" - "log" - "os" - "fmt" + "fmt" + "github.com/quii/learn-go-with-tests/command-line/v3" + "log" + "os" ) const dbFileName = "game.db.json" func main() { - store, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName) + store, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName) - if err != nil { - log.Fatal(err) - } - defer close() + if err != nil { + log.Fatal(err) + } + defer close() - fmt.Println("Let's play poker") - fmt.Println("Type {Name} wins to record a win") - poker.NewCLI(store, os.Stdin).PlayPoker() + fmt.Println("Let's play poker") + fmt.Println("Type {Name} wins to record a win") + poker.NewCLI(store, os.Stdin).PlayPoker() } ``` @@ -632,26 +632,26 @@ func main() { package main import ( - "github.com/quii/learn-go-with-tests/command-line/v3" - "log" - "net/http" + "github.com/quii/learn-go-with-tests/command-line/v3" + "log" + "net/http" ) const dbFileName = "game.db.json" func main() { - store, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName) + store, close, err := poker.FileSystemPlayerStoreFromFile(dbFileName) - if err != nil { - log.Fatal(err) - } - defer close() + if err != nil { + log.Fatal(err) + } + defer close() - server := poker.NewPlayerServer(store) + server := poker.NewPlayerServer(store) - if err := http.ListenAndServe(":5000", server); err != nil { - log.Fatalf("could not listen on port 5000 %v", err) - } + if err := http.ListenAndServe(":5000", server); err != nil { + log.Fatalf("could not listen on port 5000 %v", err) + } } ``` diff --git a/concurrency.md b/concurrency.md index 07e72d9..89cf130 100644 --- a/concurrency.md +++ b/concurrency.md @@ -11,13 +11,13 @@ package concurrency type WebsiteChecker func(string) bool func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool { - results := make(map[string]bool) + results := make(map[string]bool) - for _, url := range urls { - results[url] = wc(url) - } + for _, url := range urls { + results[url] = wc(url) + } - return results + return results } ``` @@ -36,35 +36,35 @@ Here's the test they've written: package concurrency import ( - "reflect" - "testing" + "reflect" + "testing" ) func mockWebsiteChecker(url string) bool { - if url == "waat://furhurterwe.geds" { - return false - } - return true + if url == "waat://furhurterwe.geds" { + return false + } + return true } func TestCheckWebsites(t *testing.T) { - websites := []string{ - "http://google.com", - "http://blog.gypsydave5.com", - "waat://furhurterwe.geds", - } + websites := []string{ + "http://google.com", + "http://blog.gypsydave5.com", + "waat://furhurterwe.geds", + } - want := map[string]bool{ - "http://google.com": true, - "http://blog.gypsydave5.com": true, - "waat://furhurterwe.geds": false, - } + want := map[string]bool{ + "http://google.com": true, + "http://blog.gypsydave5.com": true, + "waat://furhurterwe.geds": false, + } - got := CheckWebsites(mockWebsiteChecker, websites) + got := CheckWebsites(mockWebsiteChecker, websites) - if !reflect.DeepEqual(want, got) { - t.Fatalf("Wanted %v, got %v", want, got) - } + if !reflect.DeepEqual(want, got) { + t.Fatalf("Wanted %v, got %v", want, got) + } } ``` @@ -81,24 +81,24 @@ effect of our changes. package concurrency import ( - "testing" - "time" + "testing" + "time" ) func slowStubWebsiteChecker(_ string) bool { - time.Sleep(20 * time.Millisecond) - return true + time.Sleep(20 * time.Millisecond) + return true } func BenchmarkCheckWebsites(b *testing.B) { - urls := make([]string, 100) - for i := 0; i < len(urls); i++ { - urls[i] = "a url" - } + urls := make([]string, 100) + for i := 0; i < len(urls); i++ { + urls[i] = "a url" + } - for i := 0; i < b.N; i++ { - CheckWebsites(slowStubWebsiteChecker, urls) - } + for i := 0; i < b.N; i++ { + CheckWebsites(slowStubWebsiteChecker, urls) + } } ``` @@ -158,15 +158,15 @@ package concurrency type WebsiteChecker func(string) bool func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool { - results := make(map[string]bool) + results := make(map[string]bool) - for _, url := range urls { - go func() { - results[url] = wc(url) - }() - } + for _, url := range urls { + go func() { + results[url] = wc(url) + }() + } - return results + return results } ``` @@ -228,17 +228,17 @@ import "time" type WebsiteChecker func(string) bool func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool { - results := make(map[string]bool) + results := make(map[string]bool) - for _, url := range urls { - go func() { - results[url] = wc(url) - }() - } + for _, url := range urls { + go func() { + results[url] = wc(url) + }() + } - time.Sleep(2 * time.Second) + time.Sleep(2 * time.Second) - return results + return results } ``` @@ -266,23 +266,23 @@ To fix this: package concurrency import ( - "time" + "time" ) type WebsiteChecker func(string) bool func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool { - results := make(map[string]bool) + results := make(map[string]bool) - for _, url := range urls { - go func(u string) { - results[u] = wc(u) - }(url) - } + for _, url := range urls { + go func(u string) { + results[u] = wc(u) + }(url) + } - time.Sleep(2 * time.Second) + time.Sleep(2 * time.Second) - return results + return results } ``` @@ -405,26 +405,26 @@ package concurrency type WebsiteChecker func(string) bool type result struct { - string - bool + string + bool } func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool { - results := make(map[string]bool) - resultChannel := make(chan result) + results := make(map[string]bool) + resultChannel := make(chan result) - for _, url := range urls { - go func(u string) { - resultChannel <- result{u, wc(u)} - }(url) - } + for _, url := range urls { + go func(u string) { + resultChannel <- result{u, wc(u)} + }(url) + } - for i := 0; i < len(urls); i++ { - result := <-resultChannel - results[result.string] = result.bool - } + for i := 0; i < len(urls); i++ { + result := <-resultChannel + results[result.string] = result.bool + } - return results + return results } ``` diff --git a/dependency-injection.md b/dependency-injection.md index 5045021..2fc4689 100644 --- a/dependency-injection.md +++ b/dependency-injection.md @@ -17,7 +17,7 @@ Just to recap, here is what that function could look like ```go func Greet(name string) { - fmt.Printf("Hello, %s", name) + fmt.Printf("Hello, %s", name) } ``` @@ -34,7 +34,7 @@ If you look at the source code of `fmt.Printf` you can see a way for us to hook ```go // It returns the number of bytes written and any write error encountered. func Printf(format string, a ...interface{}) (n int, err error) { - return Fprintf(os.Stdout, format, a...) + return Fprintf(os.Stdout, format, a...) } ``` @@ -44,11 +44,11 @@ What exactly _is_ an `os.Stdout`? What does `Fprintf` expect to get passed to it ```go func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { - p := newPrinter() - p.doPrintf(format, a) - n, err = w.Write(p.buf) - p.free() - return + p := newPrinter() + p.doPrintf(format, a) + n, err = w.Write(p.buf) + p.free() + return } ``` @@ -56,7 +56,7 @@ An `io.Writer` ```go type Writer interface { - Write(p []byte) (n int, err error) + Write(p []byte) (n int, err error) } ``` @@ -68,15 +68,15 @@ So we know under the covers we're ultimately using `Writer` to send our greeting ```go func TestGreet(t *testing.T) { - buffer := bytes.Buffer{} - Greet(&buffer,"Chris") + buffer := bytes.Buffer{} + Greet(&buffer, "Chris") - got := buffer.String() - want := "Hello, Chris" + got := buffer.String() + want := "Hello, Chris" - if got != want { - t.Errorf("got %q want %q", got, want) - } + if got != want { + t.Errorf("got %q want %q", got, want) + } } ``` @@ -100,7 +100,7 @@ _Listen to the compiler_ and fix the problem. ```go func Greet(writer *bytes.Buffer, name string) { - fmt.Printf("Hello, %s", name) + fmt.Printf("Hello, %s", name) } ``` @@ -114,7 +114,7 @@ Use the writer to send the greeting to the buffer in our test. Remember `fmt.Fpr ```go func Greet(writer *bytes.Buffer, name string) { - fmt.Fprintf(writer, "Hello, %s", name) + fmt.Fprintf(writer, "Hello, %s", name) } ``` @@ -128,7 +128,7 @@ To demonstrate this, try wiring up the `Greet` function into a Go application wh ```go func main() { - Greet(os.Stdout, "Elodie") + Greet(os.Stdout, "Elodie") } ``` @@ -152,7 +152,7 @@ func Greet(writer io.Writer, name string) { } func main() { - Greet(os.Stdout, "Elodie") + Greet(os.Stdout, "Elodie") } ``` @@ -168,21 +168,21 @@ Run the following package main import ( - "fmt" - "io" - "net/http" + "fmt" + "io" + "net/http" ) func Greet(writer io.Writer, name string) { - fmt.Fprintf(writer, "Hello, %s", name) + fmt.Fprintf(writer, "Hello, %s", name) } func MyGreeterHandler(w http.ResponseWriter, r *http.Request) { - Greet(w, "world") + Greet(w, "world") } func main() { - http.ListenAndServe(":5000", http.HandlerFunc(MyGreeterHandler)) + http.ListenAndServe(":5000", http.HandlerFunc(MyGreeterHandler)) } ``` diff --git a/error-types.md b/error-types.md index 59b1766..f973f31 100644 --- a/error-types.md +++ b/error-types.md @@ -37,23 +37,23 @@ As Pedro says, we _could_ write a test for the status error like so. ```go t.Run("when you don't get a 200 you get a status error", func(t *testing.T) { - svr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - res.WriteHeader(http.StatusTeapot) - })) - defer svr.Close() + svr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(http.StatusTeapot) + })) + defer svr.Close() - _, err := DumbGetter(svr.URL) + _, err := DumbGetter(svr.URL) - if err == nil { - t.Fatal("expected an error") - } + if err == nil { + t.Fatal("expected an error") + } - want := fmt.Sprintf("did not get 200 from %s, got %d", svr.URL, http.StatusTeapot) - got := err.Error() + want := fmt.Sprintf("did not get 200 from %s, got %d", svr.URL, http.StatusTeapot) + got := err.Error() - if got != want { - t.Errorf(`got "%v", want "%v"`, got, want) - } + if got != want { + t.Errorf(`got "%v", want "%v"`, got, want) + } }) ``` @@ -95,28 +95,28 @@ Let's change our existing test to reflect this need ```go t.Run("when you don't get a 200 you get a status error", func(t *testing.T) { - svr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - res.WriteHeader(http.StatusTeapot) - })) - defer svr.Close() + svr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(http.StatusTeapot) + })) + defer svr.Close() - _, err := DumbGetter(svr.URL) + _, err := DumbGetter(svr.URL) - if err == nil { - t.Fatal("expected an error") - } + if err == nil { + t.Fatal("expected an error") + } - got, isStatusErr := err.(BadStatusError) + got, isStatusErr := err.(BadStatusError) - if !isStatusErr { - t.Fatalf("was not a BadStatusError, got %T", err) - } + if !isStatusErr { + t.Fatalf("was not a BadStatusError, got %T", err) + } - want := BadStatusError{URL:svr.URL, Status:http.StatusTeapot} + want := BadStatusError{URL: svr.URL, Status: http.StatusTeapot} - if got != want { - t.Errorf("got %v, want %v", got, want) - } + if got != want { + t.Errorf("got %v, want %v", got, want) + } }) ``` @@ -144,7 +144,7 @@ Let's fix `DumbGetter` by updating our error handling code to use our type ```go if res.StatusCode != http.StatusOK { - return "", BadStatusError{URL: url, Status: res.StatusCode} + return "", BadStatusError{URL: url, Status: res.StatusCode} } ``` @@ -169,28 +169,28 @@ As of Go 1.13 there are new ways to work with errors in the standard library whi ```go t.Run("when you don't get a 200 you get a status error", func(t *testing.T) { - svr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - res.WriteHeader(http.StatusTeapot) - })) - defer svr.Close() + svr := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(http.StatusTeapot) + })) + defer svr.Close() - _, err := DumbGetter(svr.URL) + _, err := DumbGetter(svr.URL) - if err == nil { - t.Fatal("expected an error") - } + if err == nil { + t.Fatal("expected an error") + } - var got BadStatusError - isBadStatusError := errors.As(err, &got) - want := BadStatusError{URL: svr.URL, Status: http.StatusTeapot} + var got BadStatusError + isBadStatusError := errors.As(err, &got) + want := BadStatusError{URL: svr.URL, Status: http.StatusTeapot} - if !isBadStatusError { - t.Fatalf("was not a BadStatusError, got %T", err) - } + if !isBadStatusError { + t.Fatalf("was not a BadStatusError, got %T", err) + } - if got != want { - t.Errorf("got %v, want %v", got, want) - } + if got != want { + t.Errorf("got %v, want %v", got, want) + } }) ``` diff --git a/hello-world.md b/hello-world.md index 53168b2..ab4730c 100644 --- a/hello-world.md +++ b/hello-world.md @@ -20,7 +20,7 @@ package main import "fmt" func main() { - fmt.Println("Hello, world") + fmt.Println("Hello, world") } ``` @@ -44,11 +44,11 @@ package main import "fmt" func Hello() string { - return "Hello, world" + return "Hello, world" } func main() { - fmt.Println(Hello()) + fmt.Println(Hello()) } ``` @@ -62,12 +62,12 @@ package main import "testing" func TestHello(t *testing.T) { - got := Hello() - want := "Hello, world" + got := Hello() + want := "Hello, world" - if got != want { - t.Errorf("got %q want %q", got, want) - } + if got != want { + t.Errorf("got %q want %q", got, want) + } } ``` @@ -126,12 +126,12 @@ package main import "testing" func TestHello(t *testing.T) { - got := Hello("Chris") - want := "Hello, Chris" + got := Hello("Chris") + want := "Hello, Chris" - if got != want { - t.Errorf("got %q want %q", got, want) - } + if got != want { + t.Errorf("got %q want %q", got, want) + } } ``` @@ -151,7 +151,7 @@ Edit the `Hello` function to accept an argument of type string ```go func Hello(name string) string { - return "Hello, world" + return "Hello, world" } ``` @@ -159,7 +159,7 @@ If you try and run your tests again your `main.go` will fail to compile because ```go func main() { - fmt.Println(Hello("world")) + fmt.Println(Hello("world")) } ``` @@ -175,7 +175,7 @@ Let's make the test pass by using the name argument and concatenate it with `Hel ```go func Hello(name string) string { - return "Hello, " + name + return "Hello, " + name } ``` @@ -205,7 +205,7 @@ We can now refactor our code const englishHelloPrefix = "Hello, " func Hello(name string) string { - return englishHelloPrefix + name + return englishHelloPrefix + name } ``` @@ -224,23 +224,23 @@ Start by writing a new failing test ```go func TestHello(t *testing.T) { - t.Run("saying hello to people", func(t *testing.T) { - got := Hello("Chris") - want := "Hello, Chris" + t.Run("saying hello to people", func(t *testing.T) { + got := Hello("Chris") + want := "Hello, Chris" - if got != want { - t.Errorf("got %q want %q", got, want) - } - }) + if got != want { + t.Errorf("got %q want %q", got, want) + } + }) - t.Run("say 'Hello, World' when an empty string is supplied", func(t *testing.T) { - got := Hello("") - want := "Hello, World" + t.Run("say 'Hello, World' when an empty string is supplied", func(t *testing.T) { + got := Hello("") + want := "Hello, World" - if got != want { - t.Errorf("got %q want %q", got, want) - } - }) + if got != want { + t.Errorf("got %q want %q", got, want) + } + }) } ``` @@ -260,24 +260,24 @@ We can and should refactor our tests. ```go func TestHello(t *testing.T) { - assertCorrectMessage := func(t *testing.T, got, want string) { - t.Helper() - if got != want { - t.Errorf("got %q want %q", got, want) - } - } + assertCorrectMessage := func(t *testing.T, got, want string) { + t.Helper() + if got != want { + t.Errorf("got %q want %q", got, want) + } + } - t.Run("saying hello to people", func(t *testing.T) { - got := Hello("Chris") - want := "Hello, Chris" - assertCorrectMessage(t, got, want) - }) + t.Run("saying hello to people", func(t *testing.T) { + got := Hello("Chris") + want := "Hello, Chris" + assertCorrectMessage(t, got, want) + }) - t.Run("empty string defaults to 'World'", func(t *testing.T) { - got := Hello("") - want := "Hello, World" - assertCorrectMessage(t, got, want) - }) + t.Run("empty string defaults to 'World'", func(t *testing.T) { + got := Hello("") + want := "Hello, World" + assertCorrectMessage(t, got, want) + }) } ``` @@ -294,10 +294,10 @@ Now that we have a well-written failing test, let's fix the code, using an `if`. const englishHelloPrefix = "Hello, " func Hello(name string) string { - if name == "" { - name = "World" - } - return englishHelloPrefix + name + if name == "" { + name = "World" + } + return englishHelloPrefix + name } ``` @@ -337,11 +337,11 @@ We should be confident that we can use TDD to flesh out this functionality easil Write a test for a user passing in Spanish. Add it to the existing suite. ```go - t.Run("in Spanish", func(t *testing.T) { - got := Hello("Elodie", "Spanish") - want := "Hola, Elodie" - assertCorrectMessage(t, got, want) - }) + t.Run("in Spanish", func(t *testing.T) { + got := Hello("Elodie", "Spanish") + want := "Hola, Elodie" + assertCorrectMessage(t, got, want) + }) ``` Remember not to cheat! _Test first_. When you try and run the test, the compiler _should_ complain because you are calling `Hello` with two arguments rather than one. @@ -356,10 +356,10 @@ Fix the compilation problems by adding another string argument to `Hello` ```go func Hello(name string, language string) string { - if name == "" { - name = "World" - } - return englishHelloPrefix + name + if name == "" { + name = "World" + } + return englishHelloPrefix + name } ``` @@ -381,15 +381,15 @@ We can use `if` here to check the language is equal to "Spanish" and if so chang ```go func Hello(name string, language string) string { - if name == "" { - name = "World" - } + if name == "" { + name = "World" + } - if language == "Spanish" { - return "Hola, " + name - } + if language == "Spanish" { + return "Hola, " + name + } - return englishHelloPrefix + name + return englishHelloPrefix + name } ``` @@ -403,15 +403,15 @@ const englishHelloPrefix = "Hello, " const spanishHelloPrefix = "Hola, " func Hello(name string, language string) string { - if name == "" { - name = "World" - } + if name == "" { + name = "World" + } - if language == spanish { - return spanishHelloPrefix + name - } + if language == spanish { + return spanishHelloPrefix + name + } - return englishHelloPrefix + name + return englishHelloPrefix + name } ``` @@ -425,19 +425,19 @@ You may have written something that looks roughly like this ```go func Hello(name string, language string) string { - if name == "" { - name = "World" - } + if name == "" { + name = "World" + } - if language == spanish { - return spanishHelloPrefix + name - } + if language == spanish { + return spanishHelloPrefix + name + } - if language == french { - return frenchHelloPrefix + name - } + if language == french { + return frenchHelloPrefix + name + } - return englishHelloPrefix + name + return englishHelloPrefix + name } ``` @@ -447,20 +447,20 @@ When you have lots of `if` statements checking a particular value it is common t ```go func Hello(name string, language string) string { - if name == "" { - name = "World" - } + if name == "" { + name = "World" + } - prefix := englishHelloPrefix + prefix := englishHelloPrefix - switch language { - case french: - prefix = frenchHelloPrefix - case spanish: - prefix = spanishHelloPrefix - } + switch language { + case french: + prefix = frenchHelloPrefix + case spanish: + prefix = spanishHelloPrefix + } - return prefix + name + return prefix + name } ``` @@ -472,23 +472,23 @@ You could argue that maybe our function is getting a little big. The simplest re ```go func Hello(name string, language string) string { - if name == "" { - name = "World" - } + if name == "" { + name = "World" + } - return greetingPrefix(language) + name + return greetingPrefix(language) + name } func greetingPrefix(language string) (prefix string) { - switch language { - case french: - prefix = frenchHelloPrefix - case spanish: - prefix = spanishHelloPrefix - default: - prefix = englishHelloPrefix - } - return + switch language { + case french: + prefix = frenchHelloPrefix + case spanish: + prefix = spanishHelloPrefix + default: + prefix = englishHelloPrefix + } + return } ``` diff --git a/http-handlers-revisited.md b/http-handlers-revisited.md index ebe11a0..42d9058 100644 --- a/http-handlers-revisited.md +++ b/http-handlers-revisited.md @@ -210,10 +210,10 @@ func NewUserServer(service UserService) *UserServer { return &UserServer{service: service} } -func (u *UserServer) RegisterUser(w http.ResponseWriter, r *http.Request) { +func (u *UserServer) RegisterUser(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - // request parsing and validation + // request parsing and validation var newUser User err := json.NewDecoder(r.Body).Decode(&newUser) @@ -222,10 +222,10 @@ func (u *UserServer) RegisterUser(w http.ResponseWriter, r *http.Request) { return } - // call a service thing to take care of the hard work + // call a service thing to take care of the hard work insertedID, err := u.service.Register(newUser) - // depending on what we get back, respond accordingly + // depending on what we get back, respond accordingly if err != nil { //todo: handle different kinds of errors differently http.Error(w, fmt.Sprintf("problem registering new user: %v", err), http.StatusInternalServerError) diff --git a/http-server.md b/http-server.md index b198212..02b8eff 100644 --- a/http-server.md +++ b/http-server.md @@ -57,7 +57,7 @@ This will start a web server listening on a port, creating a goroutine for every ```go type Handler interface { - ServeHTTP(ResponseWriter, *Request) + ServeHTTP(ResponseWriter, *Request) } ``` @@ -67,19 +67,19 @@ Let's write a test for a function `PlayerServer` that takes in those two argumen ```go func TestGETPlayers(t *testing.T) { - t.Run("returns Pepper's score", func(t *testing.T) { - request, _ := http.NewRequest(http.MethodGet, "/players/Pepper", nil) - response := httptest.NewRecorder() + t.Run("returns Pepper's score", func(t *testing.T) { + request, _ := http.NewRequest(http.MethodGet, "/players/Pepper", nil) + response := httptest.NewRecorder() - PlayerServer(response, request) + PlayerServer(response, request) - got := response.Body.String() - want := "20" + got := response.Body.String() + want := "20" - if got != want { - t.Errorf("got %q, want %q", got, want) - } - }) + if got != want { + t.Errorf("got %q, want %q", got, want) + } + }) } ``` @@ -134,7 +134,7 @@ From the DI chapter, we touched on HTTP servers with a `Greet` function. We lear ```go func PlayerServer(w http.ResponseWriter, r *http.Request) { - fmt.Fprint(w, "20") + fmt.Fprint(w, "20") } ``` @@ -153,15 +153,15 @@ Create a new file for our application and put this code in. package main import ( - "log" - "net/http" + "log" + "net/http" ) func main() { - handler := http.HandlerFunc(PlayerServer) - if err := http.ListenAndServe(":5000", handler); err != nil { - log.Fatalf("could not listen on port 5000 %v", err) - } + handler := http.HandlerFunc(PlayerServer) + if err := http.ListenAndServe(":5000", handler); err != nil { + log.Fatalf("could not listen on port 5000 %v", err) + } } ``` @@ -196,17 +196,17 @@ We'll add another subtest to our suite which tries to get the score of a differe ```go t.Run("returns Floyd's score", func(t *testing.T) { - request, _ := http.NewRequest(http.MethodGet, "/players/Floyd", nil) - response := httptest.NewRecorder() + request, _ := http.NewRequest(http.MethodGet, "/players/Floyd", nil) + response := httptest.NewRecorder() - PlayerServer(response, request) + PlayerServer(response, request) - got := response.Body.String() - want := "10" + got := response.Body.String() + want := "10" - if got != want { - t.Errorf("got %q, want %q", got, want) - } + if got != want { + t.Errorf("got %q, want %q", got, want) + } }) ``` @@ -230,17 +230,17 @@ Remember we are just trying to take as small as steps as reasonably possible, so ```go func PlayerServer(w http.ResponseWriter, r *http.Request) { - player := strings.TrimPrefix(r.URL.Path, "/players/") + player := strings.TrimPrefix(r.URL.Path, "/players/") - if player == "Pepper" { - fmt.Fprint(w, "20") - return - } + if player == "Pepper" { + fmt.Fprint(w, "20") + return + } - if player == "Floyd" { - fmt.Fprint(w, "10") - return - } + if player == "Floyd" { + fmt.Fprint(w, "10") + return + } } ``` @@ -258,21 +258,21 @@ We can simplify the `PlayerServer` by separating out the score retrieval into a ```go func PlayerServer(w http.ResponseWriter, r *http.Request) { - player := strings.TrimPrefix(r.URL.Path, "/players/") + player := strings.TrimPrefix(r.URL.Path, "/players/") - fmt.Fprint(w, GetPlayerScore(player)) + fmt.Fprint(w, GetPlayerScore(player)) } func GetPlayerScore(name string) string { - if name == "Pepper" { - return "20" - } + if name == "Pepper" { + return "20" + } - if name == "Floyd" { - return "10" - } + if name == "Floyd" { + return "10" + } - return "" + return "" } ``` @@ -280,35 +280,35 @@ And we can DRY up some of the code in the tests by making some helpers ```go func TestGETPlayers(t *testing.T) { - t.Run("returns Pepper's score", func(t *testing.T) { - request := newGetScoreRequest("Pepper") - response := httptest.NewRecorder() + t.Run("returns Pepper's score", func(t *testing.T) { + request := newGetScoreRequest("Pepper") + response := httptest.NewRecorder() - PlayerServer(response, request) + PlayerServer(response, request) - assertResponseBody(t, response.Body.String(), "20") - }) + assertResponseBody(t, response.Body.String(), "20") + }) - t.Run("returns Floyd's score", func(t *testing.T) { - request := newGetScoreRequest("Floyd") - response := httptest.NewRecorder() + t.Run("returns Floyd's score", func(t *testing.T) { + request := newGetScoreRequest("Floyd") + response := httptest.NewRecorder() - PlayerServer(response, request) + PlayerServer(response, request) - assertResponseBody(t, response.Body.String(), "10") - }) + assertResponseBody(t, response.Body.String(), "10") + }) } func newGetScoreRequest(name string) *http.Request { - req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/players/%s", name), nil) - return req + req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/players/%s", name), nil) + return req } func assertResponseBody(t *testing.T, got, want string) { - t.Helper() - if got != want { - t.Errorf("response body is wrong, got %q want %q", got, want) - } + t.Helper() + if got != want { + t.Errorf("response body is wrong, got %q want %q", got, want) + } } ``` @@ -322,7 +322,7 @@ Let's move our function we re-factored to be an interface instead ```go type PlayerStore interface { - GetPlayerScore(name string) int + GetPlayerScore(name string) int } ``` @@ -330,7 +330,7 @@ For our `PlayerServer` to be able to use a `PlayerStore`, it will need a referen ```go type PlayerServer struct { - store PlayerStore + store PlayerStore } ``` @@ -338,8 +338,8 @@ Finally, we will now implement the `Handler` interface by adding a method to our ```go func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - player := strings.TrimPrefix(r.URL.Path, "/players/") - fmt.Fprint(w, p.store.GetPlayerScore(player)) + player := strings.TrimPrefix(r.URL.Path, "/players/") + fmt.Fprint(w, p.store.GetPlayerScore(player)) } ``` @@ -349,16 +349,16 @@ Here is the full code listing of our server ```go type PlayerStore interface { - GetPlayerScore(name string) int + GetPlayerScore(name string) int } type PlayerServer struct { - store PlayerStore + store PlayerStore } func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - player := strings.TrimPrefix(r.URL.Path, "/players/") - fmt.Fprint(w, p.store.GetPlayerScore(player)) + player := strings.TrimPrefix(r.URL.Path, "/players/") + fmt.Fprint(w, p.store.GetPlayerScore(player)) } ``` @@ -372,25 +372,25 @@ We need to change our tests to instead create a new instance of our `PlayerServe ```go func TestGETPlayers(t *testing.T) { - server := &PlayerServer{} + server := &PlayerServer{} - t.Run("returns Pepper's score", func(t *testing.T) { - request := newGetScoreRequest("Pepper") - response := httptest.NewRecorder() + t.Run("returns Pepper's score", func(t *testing.T) { + request := newGetScoreRequest("Pepper") + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertResponseBody(t, response.Body.String(), "20") - }) + assertResponseBody(t, response.Body.String(), "20") + }) - t.Run("returns Floyd's score", func(t *testing.T) { - request := newGetScoreRequest("Floyd") - response := httptest.NewRecorder() + t.Run("returns Floyd's score", func(t *testing.T) { + request := newGetScoreRequest("Floyd") + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertResponseBody(t, response.Body.String(), "10") - }) + assertResponseBody(t, response.Body.String(), "10") + }) } ``` @@ -404,11 +404,11 @@ Now `main.go` won't compile for the same reason. ```go func main() { - server := &PlayerServer{} + server := &PlayerServer{} - if err := http.ListenAndServe(":5000", server); err != nil { - log.Fatalf("could not listen on port 5000 %v", err) - } + if err := http.ListenAndServe(":5000", server); err != nil { + log.Fatalf("could not listen on port 5000 %v", err) + } } ``` @@ -424,12 +424,12 @@ This is because we have not passed in a `PlayerStore` in our tests. We'll need t ```go type StubPlayerStore struct { - scores map[string]int + scores map[string]int } func (s *StubPlayerStore) GetPlayerScore(name string) int { - score := s.scores[name] - return score + score := s.scores[name] + return score } ``` @@ -437,31 +437,31 @@ A `map` is a quick and easy way of making a stub key/value store for our tests. ```go func TestGETPlayers(t *testing.T) { - store := StubPlayerStore{ - map[string]int{ - "Pepper": 20, - "Floyd": 10, - }, - } - server := &PlayerServer{&store} + store := StubPlayerStore{ + map[string]int{ + "Pepper": 20, + "Floyd": 10, + }, + } + server := &PlayerServer{&store} - t.Run("returns Pepper's score", func(t *testing.T) { - request := newGetScoreRequest("Pepper") - response := httptest.NewRecorder() + t.Run("returns Pepper's score", func(t *testing.T) { + request := newGetScoreRequest("Pepper") + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertResponseBody(t, response.Body.String(), "20") - }) + assertResponseBody(t, response.Body.String(), "20") + }) - t.Run("returns Floyd's score", func(t *testing.T) { - request := newGetScoreRequest("Floyd") - response := httptest.NewRecorder() + t.Run("returns Floyd's score", func(t *testing.T) { + request := newGetScoreRequest("Floyd") + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertResponseBody(t, response.Body.String(), "10") - }) + assertResponseBody(t, response.Body.String(), "10") + }) } ``` @@ -479,15 +479,15 @@ We'll need to make an implementation of one, but that's difficult right now as w type InMemoryPlayerStore struct{} func (i *InMemoryPlayerStore) GetPlayerScore(name string) int { - return 123 + return 123 } func main() { - server := &PlayerServer{&InMemoryPlayerStore{}} + server := &PlayerServer{&InMemoryPlayerStore{}} - if err := http.ListenAndServe(":5000", server); err != nil { - log.Fatalf("could not listen on port 5000 %v", err) - } + if err := http.ListenAndServe(":5000", server); err != nil { + log.Fatalf("could not listen on port 5000 %v", err) + } } ``` @@ -507,17 +507,17 @@ Add a missing player scenario to our existing suite ```go t.Run("returns 404 on missing players", func(t *testing.T) { - request := newGetScoreRequest("Apollo") - response := httptest.NewRecorder() + request := newGetScoreRequest("Apollo") + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - got := response.Code - want := http.StatusNotFound + got := response.Code + want := http.StatusNotFound - if got != want { - t.Errorf("got status %d want %d", got, want) - } + if got != want { + t.Errorf("got status %d want %d", got, want) + } }) ``` @@ -533,11 +533,11 @@ t.Run("returns 404 on missing players", func(t *testing.T) { ```go func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - player := strings.TrimPrefix(r.URL.Path, "/players/") + player := strings.TrimPrefix(r.URL.Path, "/players/") - w.WriteHeader(http.StatusNotFound) + w.WriteHeader(http.StatusNotFound) - fmt.Fprint(w, p.store.GetPlayerScore(player)) + fmt.Fprint(w, p.store.GetPlayerScore(player)) } ``` @@ -553,61 +553,61 @@ Here are the new tests ```go func TestGETPlayers(t *testing.T) { - store := StubPlayerStore{ - map[string]int{ - "Pepper": 20, - "Floyd": 10, - }, - } - server := &PlayerServer{&store} + store := StubPlayerStore{ + map[string]int{ + "Pepper": 20, + "Floyd": 10, + }, + } + server := &PlayerServer{&store} - t.Run("returns Pepper's score", func(t *testing.T) { - request := newGetScoreRequest("Pepper") - response := httptest.NewRecorder() + t.Run("returns Pepper's score", func(t *testing.T) { + request := newGetScoreRequest("Pepper") + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertStatus(t, response.Code, http.StatusOK) - assertResponseBody(t, response.Body.String(), "20") - }) + assertStatus(t, response.Code, http.StatusOK) + assertResponseBody(t, response.Body.String(), "20") + }) - t.Run("returns Floyd's score", func(t *testing.T) { - request := newGetScoreRequest("Floyd") - response := httptest.NewRecorder() + t.Run("returns Floyd's score", func(t *testing.T) { + request := newGetScoreRequest("Floyd") + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertStatus(t, response.Code, http.StatusOK) - assertResponseBody(t, response.Body.String(), "10") - }) + assertStatus(t, response.Code, http.StatusOK) + assertResponseBody(t, response.Body.String(), "10") + }) - t.Run("returns 404 on missing players", func(t *testing.T) { - request := newGetScoreRequest("Apollo") - response := httptest.NewRecorder() + t.Run("returns 404 on missing players", func(t *testing.T) { + request := newGetScoreRequest("Apollo") + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertStatus(t, response.Code, http.StatusNotFound) - }) + assertStatus(t, response.Code, http.StatusNotFound) + }) } func assertStatus(t *testing.T, got, want int) { - t.Helper() - if got != want { - t.Errorf("did not get correct status, got %d, want %d", got, want) - } + t.Helper() + if got != want { + t.Errorf("did not get correct status, got %d, want %d", got, want) + } } func newGetScoreRequest(name string) *http.Request { - req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/players/%s", name), nil) - return req + req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/players/%s", name), nil) + return req } func assertResponseBody(t *testing.T, got, want string) { - t.Helper() - if got != want { - t.Errorf("response body is wrong, got %q want %q", got, want) - } + t.Helper() + if got != want { + t.Errorf("response body is wrong, got %q want %q", got, want) + } } ``` @@ -617,15 +617,15 @@ Now our first two tests fail because of the 404 instead of 200, so we can fix `P ```go func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - player := strings.TrimPrefix(r.URL.Path, "/players/") + player := strings.TrimPrefix(r.URL.Path, "/players/") - score := p.store.GetPlayerScore(player) + score := p.store.GetPlayerScore(player) - if score == 0 { - w.WriteHeader(http.StatusNotFound) - } + if score == 0 { + w.WriteHeader(http.StatusNotFound) + } - fmt.Fprint(w, score) + fmt.Fprint(w, score) } ``` @@ -637,19 +637,19 @@ Now that we can retrieve scores from a store it now makes sense to be able to st ```go func TestStoreWins(t *testing.T) { - store := StubPlayerStore{ - map[string]int{}, - } - server := &PlayerServer{&store} + store := StubPlayerStore{ + map[string]int{}, + } + server := &PlayerServer{&store} - t.Run("it returns accepted on POST", func(t *testing.T) { - request, _ := http.NewRequest(http.MethodPost, "/players/Pepper", nil) - response := httptest.NewRecorder() + t.Run("it returns accepted on POST", func(t *testing.T) { + request, _ := http.NewRequest(http.MethodPost, "/players/Pepper", nil) + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertStatus(t, response.Code, http.StatusAccepted) - }) + assertStatus(t, response.Code, http.StatusAccepted) + }) } ``` @@ -670,20 +670,20 @@ Remember we are deliberately committing sins, so an `if` statement based on the ```go func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodPost { - w.WriteHeader(http.StatusAccepted) - return - } + if r.Method == http.MethodPost { + w.WriteHeader(http.StatusAccepted) + return + } - player := strings.TrimPrefix(r.URL.Path, "/players/") + player := strings.TrimPrefix(r.URL.Path, "/players/") - score := p.store.GetPlayerScore(player) + score := p.store.GetPlayerScore(player) - if score == 0 { - w.WriteHeader(http.StatusNotFound) - } + if score == 0 { + w.WriteHeader(http.StatusNotFound) + } - fmt.Fprint(w, score) + fmt.Fprint(w, score) } ``` @@ -694,29 +694,29 @@ The handler is looking a bit muddled now. Let's break the code up to make it eas ```go func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case http.MethodPost: - p.processWin(w) - case http.MethodGet: - p.showScore(w, r) - } + switch r.Method { + case http.MethodPost: + p.processWin(w) + case http.MethodGet: + p.showScore(w, r) + } } func (p *PlayerServer) showScore(w http.ResponseWriter, r *http.Request) { - player := strings.TrimPrefix(r.URL.Path, "/players/") + player := strings.TrimPrefix(r.URL.Path, "/players/") - score := p.store.GetPlayerScore(player) + score := p.store.GetPlayerScore(player) - if score == 0 { - w.WriteHeader(http.StatusNotFound) - } + if score == 0 { + w.WriteHeader(http.StatusNotFound) + } - fmt.Fprint(w, score) + fmt.Fprint(w, score) } func (p *PlayerServer) processWin(w http.ResponseWriter) { - w.WriteHeader(http.StatusAccepted) + w.WriteHeader(http.StatusAccepted) } ``` @@ -730,17 +730,17 @@ We can accomplish this by extending our `StubPlayerStore` with a new `RecordWin` ```go type StubPlayerStore struct { - scores map[string]int - winCalls []string + scores map[string]int + winCalls []string } func (s *StubPlayerStore) GetPlayerScore(name string) int { - score := s.scores[name] - return score + score := s.scores[name] + return score } func (s *StubPlayerStore) RecordWin(name string) { - s.winCalls = append(s.winCalls, name) + s.winCalls = append(s.winCalls, name) } ``` @@ -748,28 +748,28 @@ Now extend our test to check the number of invocations for a start ```go func TestStoreWins(t *testing.T) { - store := StubPlayerStore{ - map[string]int{}, - } - server := &PlayerServer{&store} + store := StubPlayerStore{ + map[string]int{}, + } + server := &PlayerServer{&store} - t.Run("it records wins when POST", func(t *testing.T) { - request := newPostWinRequest("Pepper") - response := httptest.NewRecorder() + t.Run("it records wins when POST", func(t *testing.T) { + request := newPostWinRequest("Pepper") + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertStatus(t, response.Code, http.StatusAccepted) + assertStatus(t, response.Code, http.StatusAccepted) - if len(store.winCalls) != 1 { - t.Errorf("got %d calls to RecordWin want %d", len(store.winCalls), 1) - } - }) + if len(store.winCalls) != 1 { + t.Errorf("got %d calls to RecordWin want %d", len(store.winCalls), 1) + } + }) } func newPostWinRequest(name string) *http.Request { - req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/players/%s", name), nil) - return req + req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/players/%s", name), nil) + return req } ``` @@ -786,8 +786,8 @@ We need to update our code where we create a `StubPlayerStore` as we've added a ```go store := StubPlayerStore{ - map[string]int{}, - nil, + map[string]int{}, + nil, } ``` @@ -805,8 +805,8 @@ We need to update `PlayerServer`'s idea of what a `PlayerStore` is by changing t ```go type PlayerStore interface { - GetPlayerScore(name string) int - RecordWin(name string) + GetPlayerScore(name string) int + RecordWin(name string) } ``` @@ -831,8 +831,8 @@ Now that `PlayerStore` has `RecordWin` we can call it within our `PlayerServer` ```go func (p *PlayerServer) processWin(w http.ResponseWriter) { - p.store.RecordWin("Bob") - w.WriteHeader(http.StatusAccepted) + p.store.RecordWin("Bob") + w.WriteHeader(http.StatusAccepted) } ``` @@ -842,22 +842,22 @@ Run the tests and it should be passing! Obviously `"Bob"` isn't exactly what we ```go t.Run("it records wins on POST", func(t *testing.T) { - player := "Pepper" + player := "Pepper" - request := newPostWinRequest(player) - response := httptest.NewRecorder() + request := newPostWinRequest(player) + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertStatus(t, response.Code, http.StatusAccepted) + assertStatus(t, response.Code, http.StatusAccepted) - if len(store.winCalls) != 1 { - t.Fatalf("got %d calls to RecordWin want %d", len(store.winCalls), 1) - } + if len(store.winCalls) != 1 { + t.Fatalf("got %d calls to RecordWin want %d", len(store.winCalls), 1) + } - if store.winCalls[0] != player { - t.Errorf("did not store correct winner got %q want %q", store.winCalls[0], player) - } + if store.winCalls[0] != player { + t.Errorf("did not store correct winner got %q want %q", store.winCalls[0], player) + } }) ``` @@ -875,9 +875,9 @@ Now that we know there is one element in our `winCalls` slice we can safely refe ```go func (p *PlayerServer) processWin(w http.ResponseWriter, r *http.Request) { - player := strings.TrimPrefix(r.URL.Path, "/players/") - p.store.RecordWin(player) - w.WriteHeader(http.StatusAccepted) + player := strings.TrimPrefix(r.URL.Path, "/players/") + p.store.RecordWin(player) + w.WriteHeader(http.StatusAccepted) } ``` @@ -889,29 +889,29 @@ We can DRY up this code a bit as we're extracting the player name the same way i ```go func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - player := strings.TrimPrefix(r.URL.Path, "/players/") + player := strings.TrimPrefix(r.URL.Path, "/players/") - switch r.Method { - case http.MethodPost: - p.processWin(w, player) - case http.MethodGet: - p.showScore(w, player) - } + switch r.Method { + case http.MethodPost: + p.processWin(w, player) + case http.MethodGet: + p.showScore(w, player) + } } func (p *PlayerServer) showScore(w http.ResponseWriter, player string) { - score := p.store.GetPlayerScore(player) + score := p.store.GetPlayerScore(player) - if score == 0 { - w.WriteHeader(http.StatusNotFound) - } + if score == 0 { + w.WriteHeader(http.StatusNotFound) + } - fmt.Fprint(w, score) + fmt.Fprint(w, score) } func (p *PlayerServer) processWin(w http.ResponseWriter, player string) { - p.store.RecordWin(player) - w.WriteHeader(http.StatusAccepted) + p.store.RecordWin(player) + w.WriteHeader(http.StatusAccepted) } ``` @@ -937,19 +937,19 @@ In the interest of brevity, I am going to show you the final refactored integrat ```go func TestRecordingWinsAndRetrievingThem(t *testing.T) { - store := InMemoryPlayerStore{} - server := PlayerServer{&store} - player := "Pepper" + store := InMemoryPlayerStore{} + server := PlayerServer{&store} + player := "Pepper" - server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) - server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) - server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) + server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) + server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) + server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) - response := httptest.NewRecorder() - server.ServeHTTP(response, newGetScoreRequest(player)) - assertStatus(t, response.Code, http.StatusOK) + response := httptest.NewRecorder() + server.ServeHTTP(response, newGetScoreRequest(player)) + assertStatus(t, response.Code, http.StatusOK) - assertResponseBody(t, response.Body.String(), "3") + assertResponseBody(t, response.Body.String(), "3") } ``` @@ -974,19 +974,19 @@ If I were to get stuck in this scenario, I would revert my changes back to the f ```go func NewInMemoryPlayerStore() *InMemoryPlayerStore { - return &InMemoryPlayerStore{map[string]int{}} + return &InMemoryPlayerStore{map[string]int{}} } -type InMemoryPlayerStore struct{ - store map[string]int +type InMemoryPlayerStore struct { + store map[string]int } func (i *InMemoryPlayerStore) RecordWin(name string) { - i.store[name]++ + i.store[name]++ } func (i *InMemoryPlayerStore) GetPlayerScore(name string) int { - return i.store[name] + return i.store[name] } ``` @@ -1000,16 +1000,16 @@ The integration test passes, now we just need to change `main` to use `NewInMemo package main import ( - "log" - "net/http" + "log" + "net/http" ) func main() { - server := &PlayerServer{NewInMemoryPlayerStore()} + server := &PlayerServer{NewInMemoryPlayerStore()} - if err := http.ListenAndServe(":5000", server); err != nil { - log.Fatalf("could not listen on port 5000 %v", err) - } + if err := http.ListenAndServe(":5000", server); err != nil { + log.Fatalf("could not listen on port 5000 %v", err) + } } ``` diff --git a/integers.md b/integers.md index d0eb85e..70f3673 100644 --- a/integers.md +++ b/integers.md @@ -14,12 +14,12 @@ package integers import "testing" func TestAdder(t *testing.T) { - sum := Add(2, 2) - expected := 4 + sum := Add(2, 2) + expected := 4 - if sum != expected { - t.Errorf("expected '%d' but got '%d'", expected, sum) - } + if sum != expected { + t.Errorf("expected '%d' but got '%d'", expected, sum) + } } ``` @@ -43,7 +43,7 @@ Write enough code to satisfy the compiler _and that's all_ - remember we want to package integers func Add(x, y int) int { - return 0 + return 0 } ``` @@ -61,7 +61,7 @@ In the strictest sense of TDD we should now write the _minimal amount of code to ```go func Add(x, y int) int { - return 4 + return 4 } ``` @@ -75,7 +75,7 @@ For now, let's fix it properly ```go func Add(x, y int) int { - return x + y + return x + y } ``` @@ -94,7 +94,7 @@ You can add documentation to functions with comments, and these will appear in G ```go // Add takes two integers and returns the sum of them. func Add(x, y int) int { - return x + y + return x + y } ``` @@ -112,9 +112,9 @@ As with typical tests, examples are functions that reside in a package's `_test. ```go func ExampleAdd() { - sum := Add(1, 5) - fmt.Println(sum) - // Output: 6 + sum := Add(1, 5) + fmt.Println(sum) + // Output: 6 } ``` diff --git a/iteration.md b/iteration.md index 69b5daf..0a6b767 100644 --- a/iteration.md +++ b/iteration.md @@ -16,12 +16,12 @@ package iteration import "testing" func TestRepeat(t *testing.T) { - repeated := Repeat("a") - expected := "aaaaa" + repeated := Repeat("a") + expected := "aaaaa" - if repeated != expected { - t.Errorf("expected %q but got %q", expected, repeated) - } + if repeated != expected { + t.Errorf("expected %q but got %q", expected, repeated) + } } ``` @@ -39,7 +39,7 @@ All you need to do right now is enough to make it compile so you can check your package iteration func Repeat(character string) string { - return "" + return "" } ``` @@ -53,18 +53,18 @@ The `for` syntax is very unremarkable and follows most C-like languages. ```go func Repeat(character string) string { - var repeated string - for i := 0; i < 5; i++ { - repeated = repeated + character - } - return repeated + var repeated string + for i := 0; i < 5; i++ { + repeated = repeated + character + } + return repeated } ``` Unlike other languages like C, Java, or JavaScript there are no parentheses surrounding the three components of the for statement and the braces `{ }` are always required. You might wonder what is happening in the row ```go - var repeated string + var repeated string ``` as we've been using `:=` so far to declare and initializing variables. However, `:=` is simply [short hand for both steps](https://gobyexample.com/variables). Here we are declaring a `string` variable only. Hence, the explicit version. We can also use `var` to declare functions, as we'll see later on. @@ -81,7 +81,7 @@ Now it's time to refactor and introduce another construct `+=` assignment operat const repeatCount = 5 func Repeat(character string) string { - var repeated string + var repeated string for i := 0; i < repeatCount; i++ { repeated += character } @@ -97,9 +97,9 @@ Writing [benchmarks](https://golang.org/pkg/testing/#hdr-Benchmarks) in Go is an ```go func BenchmarkRepeat(b *testing.B) { - for i := 0; i < b.N; i++ { - Repeat("a") - } + for i := 0; i < b.N; i++ { + Repeat("a") + } } ``` diff --git a/json.md b/json.md index 118173d..2fb879e 100644 --- a/json.md +++ b/json.md @@ -13,43 +13,43 @@ Our product owner has a new requirement; to have a new endpoint called `/league` package main import ( - "fmt" - "net/http" + "fmt" + "net/http" ) type PlayerStore interface { - GetPlayerScore(name string) int - RecordWin(name string) + GetPlayerScore(name string) int + RecordWin(name string) } type PlayerServer struct { - store PlayerStore + store PlayerStore } func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - player := r.URL.Path[len("/players/"):] + player := r.URL.Path[len("/players/"):] - switch r.Method { - case http.MethodPost: - p.processWin(w, player) - case http.MethodGet: - p.showScore(w, player) - } + switch r.Method { + case http.MethodPost: + p.processWin(w, player) + case http.MethodGet: + p.showScore(w, player) + } } func (p *PlayerServer) showScore(w http.ResponseWriter, player string) { - score := p.store.GetPlayerScore(player) + score := p.store.GetPlayerScore(player) - if score == 0 { - w.WriteHeader(http.StatusNotFound) - } + if score == 0 { + w.WriteHeader(http.StatusNotFound) + } - fmt.Fprint(w, score) + fmt.Fprint(w, score) } func (p *PlayerServer) processWin(w http.ResponseWriter, player string) { - p.store.RecordWin(player) - w.WriteHeader(http.StatusAccepted) + p.store.RecordWin(player) + w.WriteHeader(http.StatusAccepted) } ``` @@ -58,19 +58,19 @@ func (p *PlayerServer) processWin(w http.ResponseWriter, player string) { package main func NewInMemoryPlayerStore() *InMemoryPlayerStore { - return &InMemoryPlayerStore{map[string]int{}} + return &InMemoryPlayerStore{map[string]int{}} } type InMemoryPlayerStore struct { - store map[string]int + store map[string]int } func (i *InMemoryPlayerStore) RecordWin(name string) { - i.store[name]++ + i.store[name]++ } func (i *InMemoryPlayerStore) GetPlayerScore(name string) int { - return i.store[name] + return i.store[name] } ``` @@ -80,16 +80,16 @@ func (i *InMemoryPlayerStore) GetPlayerScore(name string) int { package main import ( - "log" - "net/http" + "log" + "net/http" ) func main() { - server := &PlayerServer{NewInMemoryPlayerStore()} + server := &PlayerServer{NewInMemoryPlayerStore()} - if err := http.ListenAndServe(":5000", server); err != nil { - log.Fatalf("could not listen on port 5000 %v", err) - } + if err := http.ListenAndServe(":5000", server); err != nil { + log.Fatalf("could not listen on port 5000 %v", err) + } } ``` @@ -103,17 +103,17 @@ We'll extend the existing suite as we have some useful test functions and a fake ```go func TestLeague(t *testing.T) { - store := StubPlayerStore{} - server := &PlayerServer{&store} + store := StubPlayerStore{} + server := &PlayerServer{&store} - t.Run("it returns 200 on /league", func(t *testing.T) { - request, _ := http.NewRequest(http.MethodGet, "/league", nil) - response := httptest.NewRecorder() + t.Run("it returns 200 on /league", func(t *testing.T) { + request, _ := http.NewRequest(http.MethodGet, "/league", nil) + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - assertStatus(t, response.Code, http.StatusOK) - }) + assertStatus(t, response.Code, http.StatusOK) + }) } ``` @@ -152,24 +152,24 @@ Let's commit some sins and get the tests passing in the quickest way we can, kno ```go func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - router := http.NewServeMux() + router := http.NewServeMux() - router.Handle("/league", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) + router.Handle("/league", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) - router.Handle("/players/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - player := r.URL.Path[len("/players/"):] + router.Handle("/players/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + player := r.URL.Path[len("/players/"):] - switch r.Method { - case http.MethodPost: - p.processWin(w, player) - case http.MethodGet: - p.showScore(w, player) - } - })) + switch r.Method { + case http.MethodPost: + p.processWin(w, player) + case http.MethodGet: + p.showScore(w, player) + } + })) - router.ServeHTTP(w, r) + router.ServeHTTP(w, r) } ``` @@ -187,26 +187,26 @@ The tests should now pass. ```go func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - router := http.NewServeMux() - router.Handle("/league", http.HandlerFunc(p.leagueHandler)) - router.Handle("/players/", http.HandlerFunc(p.playersHandler)) + router := http.NewServeMux() + router.Handle("/league", http.HandlerFunc(p.leagueHandler)) + router.Handle("/players/", http.HandlerFunc(p.playersHandler)) - router.ServeHTTP(w, r) + router.ServeHTTP(w, r) } func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) + w.WriteHeader(http.StatusOK) } func (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) { - player := r.URL.Path[len("/players/"):] + player := r.URL.Path[len("/players/"):] - switch r.Method { - case http.MethodPost: - p.processWin(w, player) - case http.MethodGet: - p.showScore(w, player) - } + switch r.Method { + case http.MethodPost: + p.processWin(w, player) + case http.MethodGet: + p.showScore(w, player) + } } ``` @@ -214,24 +214,24 @@ It's quite odd (and inefficient) to be setting up a router as a request comes in ```go type PlayerServer struct { - store PlayerStore - router *http.ServeMux + store PlayerStore + router *http.ServeMux } func NewPlayerServer(store PlayerStore) *PlayerServer { - p := &PlayerServer{ - store, - http.NewServeMux(), - } + p := &PlayerServer{ + store, + http.NewServeMux(), + } - p.router.Handle("/league", http.HandlerFunc(p.leagueHandler)) - p.router.Handle("/players/", http.HandlerFunc(p.playersHandler)) + p.router.Handle("/league", http.HandlerFunc(p.leagueHandler)) + p.router.Handle("/players/", http.HandlerFunc(p.playersHandler)) - return p + return p } func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - p.router.ServeHTTP(w, r) + p.router.ServeHTTP(w, r) } ``` @@ -245,22 +245,22 @@ Try changing the code to the following. ```go type PlayerServer struct { - store PlayerStore - http.Handler + store PlayerStore + http.Handler } func NewPlayerServer(store PlayerStore) *PlayerServer { - p := new(PlayerServer) + p := new(PlayerServer) - p.store = store + p.store = store - router := http.NewServeMux() - router.Handle("/league", http.HandlerFunc(p.leagueHandler)) - router.Handle("/players/", http.HandlerFunc(p.playersHandler)) + router := http.NewServeMux() + router.Handle("/league", http.HandlerFunc(p.leagueHandler)) + router.Handle("/players/", http.HandlerFunc(p.playersHandler)) - p.Handler = router + p.Handler = router - return p + return p } ``` @@ -284,8 +284,8 @@ Embedding is a very interesting language feature. You can use it with interfaces ```go type Animal interface { - Eater - Sleeper + Eater + Sleeper } ``` @@ -324,25 +324,25 @@ We'll start by trying to parse the response into something meaningful. ```go func TestLeague(t *testing.T) { - store := StubPlayerStore{} - server := NewPlayerServer(&store) + store := StubPlayerStore{} + server := NewPlayerServer(&store) - t.Run("it returns 200 on /league", func(t *testing.T) { - request, _ := http.NewRequest(http.MethodGet, "/league", nil) - response := httptest.NewRecorder() + t.Run("it returns 200 on /league", func(t *testing.T) { + request, _ := http.NewRequest(http.MethodGet, "/league", nil) + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - var got []Player + var got []Player - err := json.NewDecoder(response.Body).Decode(&got) + err := json.NewDecoder(response.Body).Decode(&got) - if err != nil { - t.Fatalf ("Unable to parse response from server %q into slice of Player, '%v'", response.Body, err) - } + if err != nil { + t.Fatalf("Unable to parse response from server %q into slice of Player, '%v'", response.Body, err) + } - assertStatus(t, response.Code, http.StatusOK) - }) + assertStatus(t, response.Code, http.StatusOK) + }) } ``` @@ -365,8 +365,8 @@ Given the JSON data model, it looks like we need an array of `Player` with some ```go type Player struct { - Name string - Wins int + Name string + Wins int } ``` @@ -397,13 +397,13 @@ Our endpoint currently does not return a body so it cannot be parsed into JSON. ```go func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) { - leagueTable := []Player{ - {"Chris", 20}, - } + leagueTable := []Player{ + {"Chris", 20}, + } - json.NewEncoder(w).Encode(leagueTable) + json.NewEncoder(w).Encode(leagueTable) - w.WriteHeader(http.StatusOK) + w.WriteHeader(http.StatusOK) } ``` @@ -424,14 +424,14 @@ It would be nice to introduce a separation of concern between our handler and ge ```go func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) { - json.NewEncoder(w).Encode(p.getLeagueTable()) - w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(p.getLeagueTable()) + w.WriteHeader(http.StatusOK) } -func (p *PlayerServer) getLeagueTable() []Player{ - return []Player{ - {"Chris", 20}, - } +func (p *PlayerServer) getLeagueTable() []Player { + return []Player{ + {"Chris", 20}, + } } ``` @@ -445,9 +445,9 @@ Update `StubPlayerStore` to let it store a league, which is just a slice of `Pla ```go type StubPlayerStore struct { - scores map[string]int - winCalls []string - league []Player + scores map[string]int + winCalls []string + league []Player } ``` @@ -456,35 +456,35 @@ Next, update our current test by putting some players in the league property of ```go func TestLeague(t *testing.T) { - t.Run("it returns the league table as JSON", func(t *testing.T) { - wantedLeague := []Player{ - {"Cleo", 32}, - {"Chris", 20}, - {"Tiest", 14}, - } + t.Run("it returns the league table as JSON", func(t *testing.T) { + wantedLeague := []Player{ + {"Cleo", 32}, + {"Chris", 20}, + {"Tiest", 14}, + } - store := StubPlayerStore{nil, nil, wantedLeague} - server := NewPlayerServer(&store) + store := StubPlayerStore{nil, nil, wantedLeague} + server := NewPlayerServer(&store) - request, _ := http.NewRequest(http.MethodGet, "/league", nil) - response := httptest.NewRecorder() + request, _ := http.NewRequest(http.MethodGet, "/league", nil) + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - var got []Player + var got []Player - err := json.NewDecoder(response.Body).Decode(&got) + err := json.NewDecoder(response.Body).Decode(&got) - if err != nil { - t.Fatalf("Unable to parse response from server %q into slice of Player, '%v'", response.Body, err) - } + if err != nil { + t.Fatalf("Unable to parse response from server %q into slice of Player, '%v'", response.Body, err) + } - assertStatus(t, response.Code, http.StatusOK) + assertStatus(t, response.Code, http.StatusOK) - if !reflect.DeepEqual(got, wantedLeague) { - t.Errorf("got %v want %v", got, wantedLeague) - } - }) + if !reflect.DeepEqual(got, wantedLeague) { + t.Errorf("got %v want %v", got, wantedLeague) + } + }) } ``` @@ -513,9 +513,9 @@ We know the data is in our `StubPlayerStore` and we've abstracted that away into ```go type PlayerStore interface { - GetPlayerScore(name string) int - RecordWin(name string) - GetLeague() []Player + GetPlayerScore(name string) int + RecordWin(name string) + GetLeague() []Player } ``` @@ -523,8 +523,8 @@ Now we can update our handler code to call that rather than returning a hard-cod ```go func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) { - json.NewEncoder(w).Encode(p.store.GetLeague()) - w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(p.store.GetLeague()) + w.WriteHeader(http.StatusOK) } ``` @@ -550,7 +550,7 @@ For `StubPlayerStore` it's pretty easy, just return the `league` field we added ```go func (s *StubPlayerStore) GetLeague() []Player { - return s.league + return s.league } ``` @@ -558,7 +558,7 @@ Here's a reminder of how `InMemoryStore` is implemented. ```go type InMemoryPlayerStore struct { - store map[string]int + store map[string]int } ``` @@ -568,7 +568,7 @@ So let's just get the compiler happy for now and live with the uncomfortable fee ```go func (i *InMemoryPlayerStore) GetLeague() []Player { - return nil + return nil } ``` @@ -582,23 +582,23 @@ The test code does not convey out intent very well and has a lot of boilerplate ```go t.Run("it returns the league table as JSON", func(t *testing.T) { - wantedLeague := []Player{ - {"Cleo", 32}, - {"Chris", 20}, - {"Tiest", 14}, - } + wantedLeague := []Player{ + {"Cleo", 32}, + {"Chris", 20}, + {"Tiest", 14}, + } - store := StubPlayerStore{nil, nil, wantedLeague} - server := NewPlayerServer(&store) + store := StubPlayerStore{nil, nil, wantedLeague} + server := NewPlayerServer(&store) - request := newLeagueRequest() - response := httptest.NewRecorder() + request := newLeagueRequest() + response := httptest.NewRecorder() - server.ServeHTTP(response, request) + server.ServeHTTP(response, request) - got := getLeagueFromResponse(t, response.Body) - assertStatus(t, response.Code, http.StatusOK) - assertLeague(t, got, wantedLeague) + got := getLeagueFromResponse(t, response.Body) + assertStatus(t, response.Code, http.StatusOK) + assertLeague(t, got, wantedLeague) }) ``` @@ -606,26 +606,26 @@ Here are the new helpers ```go func getLeagueFromResponse(t *testing.T, body io.Reader) (league []Player) { - t.Helper() - err := json.NewDecoder(body).Decode(&league) + t.Helper() + err := json.NewDecoder(body).Decode(&league) - if err != nil { - t.Fatalf("Unable to parse response from server %q into slice of Player, '%v'", body, err) - } + if err != nil { + t.Fatalf("Unable to parse response from server %q into slice of Player, '%v'", body, err) + } - return + return } func assertLeague(t *testing.T, got, want []Player) { - t.Helper() - if !reflect.DeepEqual(got, want) { - t.Errorf("got %v want %v", got, want) - } + t.Helper() + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v want %v", got, want) + } } func newLeagueRequest() *http.Request { - req, _ := http.NewRequest(http.MethodGet, "/league", nil) - return req + req, _ := http.NewRequest(http.MethodGet, "/league", nil) + return req } ``` @@ -637,7 +637,7 @@ Add this assertion to the existing test ```go if response.Result().Header.Get("content-type") != "application/json" { - t.Errorf("response did not have content-type of application/json, got %v", response.Result().Header) + t.Errorf("response did not have content-type of application/json, got %v", response.Result().Header) } ``` @@ -655,8 +655,8 @@ Update `leagueHandler` ```go func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - json.NewEncoder(w).Encode(p.store.GetLeague()) + w.Header().Set("content-type", "application/json") + json.NewEncoder(w).Encode(p.store.GetLeague()) } ``` @@ -670,10 +670,10 @@ Add a helper for `assertContentType`. const jsonContentType = "application/json" func assertContentType(t *testing.T, response *httptest.ResponseRecorder, want string) { - t.Helper() - if response.Result().Header.Get("content-type") != want { - t.Errorf("response did not have content-type of %s, got %v", want, response.Result().Header) - } + t.Helper() + if response.Result().Header.Get("content-type") != want { + t.Errorf("response did not have content-type of %s, got %v", want, response.Result().Header) + } } ``` @@ -693,33 +693,33 @@ We can use `t.Run` to break up this test a bit and we can reuse the helpers from ```go func TestRecordingWinsAndRetrievingThem(t *testing.T) { - store := NewInMemoryPlayerStore() - server := NewPlayerServer(store) - player := "Pepper" + store := NewInMemoryPlayerStore() + server := NewPlayerServer(store) + player := "Pepper" - server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) - server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) - server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) + server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) + server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) + server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player)) - t.Run("get score", func(t *testing.T) { - response := httptest.NewRecorder() - server.ServeHTTP(response, newGetScoreRequest(player)) - assertStatus(t, response.Code, http.StatusOK) + t.Run("get score", func(t *testing.T) { + response := httptest.NewRecorder() + server.ServeHTTP(response, newGetScoreRequest(player)) + assertStatus(t, response.Code, http.StatusOK) - assertResponseBody(t, response.Body.String(), "3") - }) + assertResponseBody(t, response.Body.String(), "3") + }) - t.Run("get league", func(t *testing.T) { - response := httptest.NewRecorder() - server.ServeHTTP(response, newLeagueRequest()) - assertStatus(t, response.Code, http.StatusOK) + t.Run("get league", func(t *testing.T) { + response := httptest.NewRecorder() + server.ServeHTTP(response, newLeagueRequest()) + assertStatus(t, response.Code, http.StatusOK) - got := getLeagueFromResponse(t, response.Body) - want := []Player{ - {"Pepper", 3}, - } - assertLeague(t, got, want) - }) + got := getLeagueFromResponse(t, response.Body) + want := []Player{ + {"Pepper", 3}, + } + assertLeague(t, got, want) + }) } ``` @@ -737,11 +737,11 @@ func TestRecordingWinsAndRetrievingThem(t *testing.T) { ```go func (i *InMemoryPlayerStore) GetLeague() []Player { - var league []Player - for name, wins := range i.store { - league = append(league, Player{name, wins}) - } - return league + var league []Player + for name, wins := range i.store { + league = append(league, Player{name, wins}) + } + return league } ``` diff --git a/select.md b/select.md index 2af06d8..ce4f212 100644 --- a/select.md +++ b/select.md @@ -17,15 +17,15 @@ Let's start with something naive to get us going. ```go func TestRacer(t *testing.T) { - slowURL := "http://www.facebook.com" - fastURL := "http://www.quii.co.uk" + slowURL := "http://www.facebook.com" + fastURL := "http://www.quii.co.uk" - want := fastURL - got := Racer(slowURL, fastURL) + want := fastURL + got := Racer(slowURL, fastURL) - if got != want { - t.Errorf("got %q, want %q", got, want) - } + if got != want { + t.Errorf("got %q, want %q", got, want) + } } ``` @@ -39,7 +39,7 @@ We know this isn't perfect and has problems but it will get us going. It's impor ```go func Racer(a, b string) (winner string) { - return + return } ``` @@ -49,19 +49,19 @@ func Racer(a, b string) (winner string) { ```go func Racer(a, b string) (winner string) { - startA := time.Now() - http.Get(a) - aDuration := time.Since(startA) + startA := time.Now() + http.Get(a) + aDuration := time.Since(startA) - startB := time.Now() - http.Get(b) - bDuration := time.Since(startB) + startB := time.Now() + http.Get(b) + bDuration := time.Since(startB) - if aDuration < bDuration { - return a - } + if aDuration < bDuration { + return a + } - return b + return b } ``` @@ -92,27 +92,27 @@ Let's change our tests to use mocks so we have reliable servers to test against ```go func TestRacer(t *testing.T) { - slowServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - time.Sleep(20 * time.Millisecond) - w.WriteHeader(http.StatusOK) - })) + slowServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + time.Sleep(20 * time.Millisecond) + w.WriteHeader(http.StatusOK) + })) - fastServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) + fastServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) - slowURL := slowServer.URL - fastURL := fastServer.URL + slowURL := slowServer.URL + fastURL := fastServer.URL - want := fastURL - got := Racer(slowURL, fastURL) + want := fastURL + got := Racer(slowURL, fastURL) - if got != want { - t.Errorf("got %q, want %q", got, want) - } + if got != want { + t.Errorf("got %q, want %q", got, want) + } - slowServer.Close() - fastServer.Close() + slowServer.Close() + fastServer.Close() } ``` @@ -136,20 +136,20 @@ We have some duplication in both our production code and test code. ```go func Racer(a, b string) (winner string) { - aDuration := measureResponseTime(a) - bDuration := measureResponseTime(b) + aDuration := measureResponseTime(a) + bDuration := measureResponseTime(b) - if aDuration < bDuration { - return a - } + if aDuration < bDuration { + return a + } - return b + return b } func measureResponseTime(url string) time.Duration { - start := time.Now() - http.Get(url) - return time.Since(start) + start := time.Now() + http.Get(url) + return time.Since(start) } ``` @@ -158,28 +158,28 @@ This DRY-ing up makes our `Racer` code a lot easier to read. ```go func TestRacer(t *testing.T) { - slowServer := makeDelayedServer(20 * time.Millisecond) - fastServer := makeDelayedServer(0 * time.Millisecond) + slowServer := makeDelayedServer(20 * time.Millisecond) + fastServer := makeDelayedServer(0 * time.Millisecond) - defer slowServer.Close() - defer fastServer.Close() + defer slowServer.Close() + defer fastServer.Close() - slowURL := slowServer.URL - fastURL := fastServer.URL + slowURL := slowServer.URL + fastURL := fastServer.URL - want := fastURL - got := Racer(slowURL, fastURL) + want := fastURL + got := Racer(slowURL, fastURL) - if got != want { - t.Errorf("got %q, want %q", got, want) - } + if got != want { + t.Errorf("got %q, want %q", got, want) + } } func makeDelayedServer(delay time.Duration) *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - time.Sleep(delay) - w.WriteHeader(http.StatusOK) - })) + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + time.Sleep(delay) + w.WriteHeader(http.StatusOK) + })) } ``` @@ -204,21 +204,21 @@ To do this, we're going to introduce a new construct called `select` which helps ```go func Racer(a, b string) (winner string) { - select { - case <-ping(a): - return a - case <-ping(b): - return b - } + select { + case <-ping(a): + return a + case <-ping(b): + return b + } } func ping(url string) chan struct{} { - ch := make(chan struct{}) - go func() { - http.Get(url) - close(ch) - }() - return ch + ch := make(chan struct{}) + go func() { + http.Get(url) + close(ch) + }() + return ch } ``` @@ -258,17 +258,17 @@ Our final requirement was to return an error if `Racer` takes longer than 10 sec ```go t.Run("returns an error if a server doesn't respond within 10s", func(t *testing.T) { - serverA := makeDelayedServer(11 * time.Second) - serverB := makeDelayedServer(12 * time.Second) + serverA := makeDelayedServer(11 * time.Second) + serverB := makeDelayedServer(12 * time.Second) - defer serverA.Close() - defer serverB.Close() + defer serverA.Close() + defer serverB.Close() - _, err := Racer(serverA.URL, serverB.URL) + _, err := Racer(serverA.URL, serverB.URL) - if err == nil { - t.Error("expected an error but didn't get one") - } + if err == nil { + t.Error("expected an error but didn't get one") + } }) ``` @@ -282,12 +282,12 @@ We've made our test servers take longer than 10s to return to exercise this scen ```go func Racer(a, b string) (winner string, error error) { - select { - case <-ping(a): - return a, nil - case <-ping(b): - return b, nil - } + select { + case <-ping(a): + return a, nil + case <-ping(b): + return b, nil + } } ``` @@ -307,14 +307,14 @@ If you run it now after 11 seconds it will fail. ```go func Racer(a, b string) (winner string, error error) { - select { - case <-ping(a): - return a, nil - case <-ping(b): - return b, nil - case <-time.After(10 * time.Second): - return "", fmt.Errorf("timed out waiting for %s and %s", a, b) - } + select { + case <-ping(a): + return a, nil + case <-ping(b): + return b, nil + case <-time.After(10 * time.Second): + return "", fmt.Errorf("timed out waiting for %s and %s", a, b) + } } ``` @@ -330,14 +330,14 @@ What we can do is make the timeout configurable. So in our test, we can have a v ```go func Racer(a, b string, timeout time.Duration) (winner string, error error) { - select { - case <-ping(a): - return a, nil - case <-ping(b): - return b, nil - case <-time.After(timeout): - return "", fmt.Errorf("timed out waiting for %s and %s", a, b) - } + select { + case <-ping(a): + return a, nil + case <-ping(b): + return b, nil + case <-time.After(timeout): + return "", fmt.Errorf("timed out waiting for %s and %s", a, b) + } } ``` @@ -354,18 +354,18 @@ Given this knowledge, let's do a little refactoring to be sympathetic to both ou var tenSecondTimeout = 10 * time.Second func Racer(a, b string) (winner string, error error) { - return ConfigurableRacer(a, b, tenSecondTimeout) + return ConfigurableRacer(a, b, tenSecondTimeout) } func ConfigurableRacer(a, b string, timeout time.Duration) (winner string, error error) { - select { - case <-ping(a): - return a, nil - case <-ping(b): - return b, nil - case <-time.After(timeout): - return "", fmt.Errorf("timed out waiting for %s and %s", a, b) - } + select { + case <-ping(a): + return a, nil + case <-ping(b): + return b, nil + case <-time.After(timeout): + return "", fmt.Errorf("timed out waiting for %s and %s", a, b) + } } ``` @@ -374,39 +374,39 @@ Our users and our first test can use `Racer` (which uses `ConfigurableRacer` und ```go func TestRacer(t *testing.T) { - t.Run("compares speeds of servers, returning the url of the fastest one", func(t *testing.T) { - slowServer := makeDelayedServer(20 * time.Millisecond) - fastServer := makeDelayedServer(0 * time.Millisecond) + t.Run("compares speeds of servers, returning the url of the fastest one", func(t *testing.T) { + slowServer := makeDelayedServer(20 * time.Millisecond) + fastServer := makeDelayedServer(0 * time.Millisecond) - defer slowServer.Close() - defer fastServer.Close() + defer slowServer.Close() + defer fastServer.Close() - slowURL := slowServer.URL - fastURL := fastServer.URL + slowURL := slowServer.URL + fastURL := fastServer.URL - want := fastURL - got, err := Racer(slowURL, fastURL) + want := fastURL + got, err := Racer(slowURL, fastURL) - if err != nil { - t.Fatalf("did not expect an error but got one %v", err) - } + if err != nil { + t.Fatalf("did not expect an error but got one %v", err) + } - if got != want { - t.Errorf("got %q, want %q", got, want) - } - }) + if got != want { + t.Errorf("got %q, want %q", got, want) + } + }) - t.Run("returns an error if a server doesn't respond within 10s", func(t *testing.T) { - server := makeDelayedServer(25 * time.Millisecond) + t.Run("returns an error if a server doesn't respond within 10s", func(t *testing.T) { + server := makeDelayedServer(25 * time.Millisecond) - defer server.Close() + defer server.Close() - _, err := ConfigurableRacer(server.URL, server.URL, 20*time.Millisecond) + _, err := ConfigurableRacer(server.URL, server.URL, 20*time.Millisecond) - if err == nil { - t.Error("expected an error but didn't get one") - } - }) + if err == nil { + t.Error("expected an error but didn't get one") + } + }) } ```