Skip to content

Commit

Permalink
change from master to main
Browse files Browse the repository at this point in the history
  • Loading branch information
quii committed Jun 18, 2020
1 parent 346c0ce commit 92879fe
Show file tree
Hide file tree
Showing 27 changed files with 195 additions and 195 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

[Art by Denise](https://twitter.com/deniseyu21)

[![Build Status](https://travis-ci.org/quii/learn-go-with-tests.svg?branch=master)](https://travis-ci.org/quii/learn-go-with-tests)
[![Build Status](https://travis-ci.org/quii/learn-go-with-tests.svg?branch=main)](https://travis-ci.org/quii/learn-go-with-tests)
[![Go Report Card](https://goreportcard.com/badge/github.com/quii/learn-go-with-tests)](https://goreportcard.com/report/github.com/quii/learn-go-with-tests)

## Formats
Expand All @@ -16,7 +16,7 @@

## Translations

- [中文](https://studygolang.gitbook.io/learn-go-with-tests)
- [中文](https://studygolang.gitbook.io/learn-go-with-tests)
- [Português](https://larien.gitbook.io/aprenda-go-com-testes/)
- [日本語](https://andmorefine.gitbook.io/learn-go-with-tests/)

Expand Down
8 changes: 4 additions & 4 deletions arrays-and-slices.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Arrays and slices

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/arrays)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/arrays)**

Arrays allow you to store multiple elements of the same type in a variable in
a particular order.
Expand Down Expand Up @@ -559,10 +559,10 @@ Another handy way to experiment with Go other than writing tests is the Go
playground. You can try most things out and you can easily share your code if
you need to ask questions. [I have made a go playground with a slice in it for you to experiment with.](https://play.golang.org/p/ICCWcRGIO68)

[Here is an example](https://play.golang.org/p/bTrRmYfNYCp) of slicing an array
and how changing the slice affects the original array; but a "copy" of the slice
[Here is an example](https://play.golang.org/p/bTrRmYfNYCp) of slicing an array
and how changing the slice affects the original array; but a "copy" of the slice
will not affect the original array.
[Another example](https://play.golang.org/p/Poth8JS28sc) of why it's a good idea
[Another example](https://play.golang.org/p/Poth8JS28sc) of why it's a good idea
to make a copy of a slice after slicing a very large slice.

[for]: ../iteration.md#
Expand Down
2 changes: 1 addition & 1 deletion command-line.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Command line and project structure

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/command-line)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/command-line)**

Our product owner now wants to _pivot_ by introducing a second application - a command line application.

Expand Down
2 changes: 1 addition & 1 deletion concurrency.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Concurrency

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/concurrency)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/concurrency)**

Here's the setup: a colleague has written a function, `CheckWebsites`, that
checks the status of a list of URLs.
Expand Down
2 changes: 1 addition & 1 deletion context-aware-reader.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Context-aware readers

**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/master/q-and-a/context-aware-reader)**
**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/main/q-and-a/context-aware-reader)**

This chapter demonstrates how to test-drive a context aware `io.Reader` as written by Mat Ryer and David Hernandez in [The Pace Dev Blog](https://pace.dev/blog/2020/02/03/context-aware-ioreader-for-golang-by-mat-ryer).

Expand Down
62 changes: 31 additions & 31 deletions context.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Context

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/context)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/context)**

Software often kicks off long-running, resource-intensive processes (often in goroutines). If the action that caused this gets cancelled or fails for some reason you need to stop these processes in a consistent way through your application.
Software often kicks off long-running, resource-intensive processes (often in goroutines). If the action that caused this gets cancelled or fails for some reason you need to stop these processes in a consistent way through your application.

If you don't manage this your snappy Go application that you're so proud of could start having difficult to debug performance problems.
If you don't manage this your snappy Go application that you're so proud of could start having difficult to debug performance problems.

In this chapter we'll use the package `context` to help us manage long-running processes.

We're going to start with a classic example of a web server that when hit kicks off a potentially long-running process to fetch some data for it to return in the response.
We're going to start with a classic example of a web server that when hit kicks off a potentially long-running process to fetch some data for it to return in the response.

We will exercise a scenario where a user cancels the request before the data can be retrieved and we'll make sure the process is told to give up.
We will exercise a scenario where a user cancels the request before the data can be retrieved and we'll make sure the process is told to give up.

I've set up some code on the happy path to get us started. Here is our server code.

Expand Down Expand Up @@ -95,17 +95,17 @@ Let's add a new test where we cancel the request before 100 milliseconds and che
t.Run("tells store to cancel work if request is cancelled", func(t *testing.T) {
store := &SpyStore{response: data}
svr := Server(store)

request := httptest.NewRequest(http.MethodGet, "/", nil)

cancellingCtx, cancel := context.WithCancel(request.Context())
time.AfterFunc(5 * time.Millisecond, cancel)
request = request.WithContext(cancellingCtx)

response := httptest.NewRecorder()

svr.ServeHTTP(response, request)

if !store.cancelled {
t.Errorf("store was not told to cancel")
}
Expand All @@ -116,7 +116,7 @@ From the [Go Blog: Context](https://blog.golang.org/context)

> The context package provides functions to derive new Context values from existing ones. These values form a tree: when a Context is canceled, all Contexts derived from it are also canceled.
It's important that you derive your contexts so that cancellations are propagated throughout the call stack for a given request.
It's important that you derive your contexts so that cancellations are propagated throughout the call stack for a given request.

What we do is derive a new `cancellingCtx` from our `request` which returns us a `cancel` function. We then schedule that function to be called in 5 milliseconds by using `time.AfterFunc`. Finally we use this new context in our request by calling `request.WithContext`.

Expand All @@ -132,7 +132,7 @@ The test fails as we'd expect.

## Write enough code to make it pass

Remember to be disciplined with TDD. Write the _minimal_ amount of code to make our test pass.
Remember to be disciplined with TDD. Write the _minimal_ amount of code to make our test pass.

```go
func Server(store Store) http.HandlerFunc {
Expand All @@ -143,11 +143,11 @@ func Server(store Store) http.HandlerFunc {
}
```

This makes this test pass but it doesn't feel good does it! We surely shouldn't be cancelling `Store` before we fetch on _every request_.
This makes this test pass but it doesn't feel good does it! We surely shouldn't be cancelling `Store` before we fetch on _every request_.

By being disciplined it highlighted a flaw in our tests, this is a good thing!
By being disciplined it highlighted a flaw in our tests, this is a good thing!

We'll need to update our happy path test to assert that it does not get cancelled.
We'll need to update our happy path test to assert that it does not get cancelled.

```go
t.Run("returns data from store", func(t *testing.T) {
Expand All @@ -162,7 +162,7 @@ t.Run("returns data from store", func(t *testing.T) {
if response.Body.String() != data {
t.Errorf(`got "%s", want "%s"`, response.Body.String(), data)
}

if store.cancelled {
t.Error("it should not have cancelled the store")
}
Expand Down Expand Up @@ -218,7 +218,7 @@ func (s *SpyStore) assertWasNotCancelled() {
}
```

Remember to pass in the `*testing.T` when creating the spy.
Remember to pass in the `*testing.T` when creating the spy.

```go
func TestServer(t *testing.T) {
Expand Down Expand Up @@ -259,11 +259,11 @@ func TestServer(t *testing.T) {
}
```

This approach is ok, but is it idiomatic?
This approach is ok, but is it idiomatic?

Does it make sense for our web server to be concerned with manually cancelling `Store`? What if `Store` also happens to depend on other slow-running processes? We'll have to make sure that `Store.Cancel` correctly propagates the cancellation to all of its dependants.
Does it make sense for our web server to be concerned with manually cancelling `Store`? What if `Store` also happens to depend on other slow-running processes? We'll have to make sure that `Store.Cancel` correctly propagates the cancellation to all of its dependants.

One of the main points of `context` is that it is a consistent way of offering cancellation.
One of the main points of `context` is that it is a consistent way of offering cancellation.

[From the go doc](https://golang.org/pkg/context/)

Expand All @@ -281,7 +281,7 @@ Feeling a bit uneasy? Good. Let's try and follow that approach though and instea

We'll have to change our existing tests as their responsibilities are changing. The only thing our handler is responsible for now is making sure it sends a context through to the downstream `Store` and that it handles the error that will come from the `Store` when it is cancelled.

Let's update our `Store` interface to show the new responsibilities.
Let's update our `Store` interface to show the new responsibilities.

```go
type Store interface {
Expand Down Expand Up @@ -333,13 +333,13 @@ func (s *SpyStore) Fetch(ctx context.Context) (string, error) {
}
```

We have to make our spy act like a real method that works with `context`.
We have to make our spy act like a real method that works with `context`.

We are simulating a slow process where we build the result slowly by appending the string, character by character in a goroutine. When the goroutine finishes its work it writes the string to the `data` channel. The goroutine listens for the `ctx.Done` and will stop the work if a signal is sent in that channel.
We are simulating a slow process where we build the result slowly by appending the string, character by character in a goroutine. When the goroutine finishes its work it writes the string to the `data` channel. The goroutine listens for the `ctx.Done` and will stop the work if a signal is sent in that channel.

Finally the code uses another `select` to wait for that goroutine to finish its work or for the cancellation to occur.

It's similar to our approach from before, we use Go's concurrency primitives to make two asynchronous processes race each other to determine what we return.
It's similar to our approach from before, we use Go's concurrency primitives to make two asynchronous processes race each other to determine what we return.

You'll take a similar approach when writing your own functions and methods that accept a `context` so make sure you understand what's going on.

Expand Down Expand Up @@ -385,7 +385,7 @@ Our happy path should be... happy. Now we can fix the other test.

## Write the test first

We need to test that we do not write any kind of response on the error case. Sadly `httptest.ResponseRecorder` doesn't have a way of figuring this out so we'll have to role our own spy to test for this.
We need to test that we do not write any kind of response on the error case. Sadly `httptest.ResponseRecorder` doesn't have a way of figuring this out so we'll have to role our own spy to test for this.

```go
type SpyResponseWriter struct {
Expand Down Expand Up @@ -450,13 +450,13 @@ func Server(store Store) http.HandlerFunc {
if err != nil {
return // todo: log error however you like
}

fmt.Fprint(w, data)
}
}
```

We can see after this that the server code has become simplified as it's no longer explicitly responsible for cancellation, it simply passes through `context` and relies on the downstream functions to respect any cancellations that may occur.
We can see after this that the server code has become simplified as it's no longer explicitly responsible for cancellation, it simply passes through `context` and relies on the downstream functions to respect any cancellations that may occur.

## Wrapping up

Expand All @@ -474,13 +474,13 @@ We can see after this that the server code has become simplified as it's no long

> If you use ctx.Value in my (non-existent) company, you’re fired
Some engineers have advocated passing values through `context` as it _feels convenient_.
Some engineers have advocated passing values through `context` as it _feels convenient_.

Convenience is often the cause of bad code.
Convenience is often the cause of bad code.

The problem with `context.Values` is that it's just an untyped map so you have no type-safety and you have to handle it not actually containing your value. You have to create a coupling of map keys from one module to another and if someone changes something things start breaking.
The problem with `context.Values` is that it's just an untyped map so you have no type-safety and you have to handle it not actually containing your value. You have to create a coupling of map keys from one module to another and if someone changes something things start breaking.

In short, **if a function needs some values, put them as typed parameters rather than trying to fetch them from `context.Value`**. This makes is statically checked and documented for everyone to see.
In short, **if a function needs some values, put them as typed parameters rather than trying to fetch them from `context.Value`**. This makes is statically checked and documented for everyone to see.

#### But...

Expand Down
2 changes: 1 addition & 1 deletion dependency-injection.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Dependency Injection

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/di)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/di)**

It is assumed that you have read the structs section before as some understanding of interfaces will be needed for this.

Expand Down
2 changes: 1 addition & 1 deletion error-types.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Error types

**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/master/q-and-a/error-types)**
**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/main/q-and-a/error-types)**

**Creating your own types for errors can be an elegant way of tidying up your code, making your code easier to use and test.**

Expand Down
4 changes: 2 additions & 2 deletions hello-world.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Hello, World

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/hello-world)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/hello-world)**

It is traditional for your first program in a new language to be [Hello, World](https://en.m.wikipedia.org/wiki/%22Hello,_World!%22_program).
It is traditional for your first program in a new language to be [Hello, World](https://en.m.wikipedia.org/wiki/%22Hello,_World!%22_program).

In the [previous chapter](install-go.md#go-environment) we discussed how Go is opinionated as to where you put your files.

Expand Down
2 changes: 1 addition & 1 deletion http-handlers-revisited.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# HTTP Handlers Revisited

**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/master/q-and-a/http-handlers-revisited)**
**[You can find all the code here](https://github.com/quii/learn-go-with-tests/tree/main/q-and-a/http-handlers-revisited)**

This book already has a chapter on [testing a HTTP handler](http-server.md) but this will feature a broader discussion on designing them, so they are simple to test.

Expand Down
2 changes: 1 addition & 1 deletion http-server.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# HTTP Server

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/http-server)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/http-server)**

You have been asked to create a web server where users can track how many games players have won.

Expand Down
2 changes: 1 addition & 1 deletion integers.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Integers

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/integers)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/integers)**

Integers work as you would expect. Let's write an `Add` function to try things out. Create a test file called `adder_test.go` and write this code.

Expand Down
4 changes: 2 additions & 2 deletions io.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# IO and sorting

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/io)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/io)**

[In the previous chapter](json.md) we continued iterating on our application by adding a new endpoint `/league`. Along the way we learned about how to deal with JSON, embedding types and routing.

Expand Down Expand Up @@ -829,7 +829,7 @@ func NewFileSystemPlayerStore(database io.ReadWriteSeeker) *FileSystemPlayerStor

Finally, we can get the amazing payoff we wanted by removing the `Seek` call from `RecordWin`. Yes, it doesn't feel much, but at least it means if we do any other kind of writes we can rely on our `Write` to behave how we need it to. Plus it will now let us test the potentially problematic code separately and fix it.

Let's write the test where we want to update the entire contents of a file with something that is smaller than the original contents.
Let's write the test where we want to update the entire contents of a file with something that is smaller than the original contents.

## Write the test first

Expand Down
2 changes: 1 addition & 1 deletion iteration.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Iteration

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/for)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/for)**

To do stuff repeatedly in Go, you'll need `for`. In Go there are no `while`, `do`, `until` keywords, you can only use `for`. Which is a good thing!

Expand Down
2 changes: 1 addition & 1 deletion json.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# JSON, routing & embedding

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/json)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/json)**

[In the previous chapter](http-server.md) we created a web server to store how many games players have won.

Expand Down
2 changes: 1 addition & 1 deletion maps.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Maps

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/maps)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/maps)**

In [arrays & slices](arrays-and-slices.md), you saw how to store values in order. Now, we will look at a way to store items by a `key` and look them up quickly.

Expand Down
2 changes: 1 addition & 1 deletion math.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Mathematics

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/math)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/math)**

For all the power of modern computers to perform huge sums at
lightning speed, the average developer rarely uses any mathematics
Expand Down
2 changes: 1 addition & 1 deletion mocking.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Mocking

**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/master/mocking)**
**[You can find all the code for this chapter here](https://github.com/quii/learn-go-with-tests/tree/main/mocking)**

You have been asked to write a program which counts down from 3, printing each number on a new line (with a 1 second pause) and when it reaches zero it will print "Go!" and exit.

Expand Down
Loading

0 comments on commit 92879fe

Please sign in to comment.