# Introdução

![](/files/KGPsFrozFxmlxjWJwWHH)

[Arte por Denise](https://twitter.com/deniseyu21)

![Build Status](https://travis-ci.org/larien/aprenda-go-com-testes.svg?main) [![Go Report Card](https://goreportcard.com/badge/github.com/larien/aprenda-go-com-testes)](https://goreportcard.com/report/github.com/larien/aprenda-go-com-testes)

* Formatos: [Gitbook](https://larien.gitbook.io/aprenda-go-com-testes), [EPUB ou PDF](https://github.com/larien/aprenda-go-com-testes/releases)
* Versão original: [English](https://quii.gitbook.io/learn-go-with-tests/)

## Motivação

* Explore a linguagem Go escrevendo testes
* **Tenha uma base com TDD**. O Go é uma boa linguagem para aprender TDD por ser simples de aprender e ter testes nativamente
* Tenha confiança de que você será capaz de escrever sistemas robustos e bem testados em Go
* [Assista a um vídeo ou leia sobre o motivo pelo qual testes unitários e TDD são importantes](/aprenda-go-com-testes/main/meta/motivacao)

## Índice

### Primeiros Passos com Go

1. [Instalação do Go](/aprenda-go-com-testes/main/primeiros-passos-com-go/instalacao-do-go) - Prepare o ambiente para produtividade.
2. [Olá, mundo](/aprenda-go-com-testes/main/primeiros-passos-com-go/ola-mundo) - Declarando variáveis, constantes, declarações `if`/`else`, switch, escreva seu primeiro programa em Go e seu primeiro teste. Sintaxe de subteste e closures.
3. [Inteiros](/aprenda-go-com-testes/main/primeiros-passos-com-go/inteiros) - Mais conteúdo sobre sintaxe de declaração de função e aprenda novas formas de melhorar a documentação do seu código.
4. [Iteração](/aprenda-go-com-testes/main/primeiros-passos-com-go/iteracao) - Aprenda sobre `for` e benchmarking.
5. [Arrays e slices](/aprenda-go-com-testes/main/primeiros-passos-com-go/arrays-e-slices) - Aprenda sobre arrays, slices, `len`, variáveis recebidas como argumentos, `range` e cobertura de testes.
6. [Estruturas, métodos e interfaces](/aprenda-go-com-testes/main/primeiros-passos-com-go/estruturas-metodos-e-interfaces) - Aprenda sobre `structs`, métodos, `interface` e testes orientados a tabela (table driven tests).
7. [Ponteiros e erros](/aprenda-go-com-testes/main/primeiros-passos-com-go/ponteiros-e-erros) - Aprenda sobre ponteiros e erros.
8. [Maps](/aprenda-go-com-testes/main/primeiros-passos-com-go/maps) - Aprenda sobre armazenamento de valores na estrutura de dados `map`.
9. [Injeção de dependência](/aprenda-go-com-testes/main/primeiros-passos-com-go/injecao-de-dependencia) - Aprenda sobre injeção de dependência, qual sua relação com interfaces e uma introdução a I/O.
10. [Mocks](/aprenda-go-com-testes/main/primeiros-passos-com-go/mocks) - Use injeção de dependência com mocks para testar um código não testado.
11. [Concorrência](/aprenda-go-com-testes/main/primeiros-passos-com-go/concorrencia) - Aprenda como escrever código concorrente para tornar seu software mais rápido.
12. [Select](/aprenda-go-com-testes/main/primeiros-passos-com-go/select) - Aprenda a sincronizar processos assíncronos de forma elegante.
13. [Reflexão](/aprenda-go-com-testes/main/primeiros-passos-com-go/reflection) - Aprenda sobre reflexão.
14. [Sync](/aprenda-go-com-testes/main/primeiros-passos-com-go/sync) - Conheça algumas funcionalidades do pacote `sync`, como `WaitGroup` e `Mutex`.
15. [Contexto](/aprenda-go-com-testes/main/primeiros-passos-com-go/contexto) - Use o pacote `context` para gerenciar e cancelar processos de longa duração.

### Criando uma aplicação

Agora que você já deu seus *Primeiros Passos com Go*, esperamos que você tenha uma base sólida das principais funcionalidades da linguagem e como TDD funciona.

Essa seção envolve a criação de uma aplicação.

Cada capítulo é uma continuação do anterior, expandindo as funcionalidades da aplicação conforme nosso "Product Owner" dita.

Novos conceitos serão apresentados para ajudar a escrever código de qualidade, mas a maior parte do material novo terá relação com o que pode ser feito com a biblioteca padrão do Go.

No final desse capítulo, você deverá ter uma boa ideia de como escrever uma aplicação em Go testada.

* [Servidor HTTP](/aprenda-go-com-testes/main/criando-uma-aplicacao/servidor-http) - Vamos criar uma aplicação que espera por requisições HTTP e as responde.
* [JSON, routing e aninhamento](/aprenda-go-com-testes/main/criando-uma-aplicacao/json) - Vamos fazer nossos endpoints retornarem JSON e explorar como trabalhar com rotas.
* [IO e classificação](/aprenda-go-com-testes/main/criando-uma-aplicacao/io) - Vamos persistir e ler nossos dados do disco e falar sobre classificação de dados.
* [Linha de comando e estrutura do projeto](/aprenda-go-com-testes/main/criando-uma-aplicacao/linha-de-comando) - Suportar diversas aplicações em uma base de código e ler entradas da linha de comando.
* [Tempo](/aprenda-go-com-testes/main/criando-uma-aplicacao/time) - Usar o pacote `time` para programar atividades.
* [Websockets](/aprenda-go-com-testes/main/criando-uma-aplicacao/websockets) - Aprender a escrever e testar um servidor que usa websockets.

### Dúvidas e respostas

Costumo ver perguntas nas Interwebs como:

> Como testo minha função incrível que faz x, y e z?

Se tiver esse tipo de dúvida, crie uma Issue no GitHub e vou tentar achar tempo para escrever um pequeno capítulo para resolver o problema. Acho que conteúdo como esse é valioso, já que está resolvendo problemas `reais` envolvendo testes que as pessoas têm.

* [OS exec](/aprenda-go-com-testes/main/perguntas-e-respostas/os-exec) - Um exemplo de como podemos usar o sistema operacional para executar comandos para buscar dados e manter nossa lógica de negócio testável.
* [Tipos de erro](/aprenda-go-com-testes/main/perguntas-e-respostas/error-types) - Exemplo de como criar seus próprios tipos de erro para melhorar seus testes e tornar seu código mais fácil de se trabalhar.

## Contribuição

* *Esse projeto está em desenvolvimento*, tanto seu conteúdo original quanto sua tradução. Se tiver interesse em contribuir, por favor entre em contato.
* Leia [contribuindo.md](/aprenda-go-com-testes/main/meta/contribuindo) para algumas diretrizes.
* Quer ajudar com a tradução para o português? Leia [traduzindo.md](/aprenda-go-com-testes/main/meta/traduzindo) e entenda como o processo de tradução está organizado.
* Tem ideias? Crie uma issue!

## Explicação

Tenho experiência em apresentar Go a equipes de desenvolvimento e tenho testado abordagens diferentes sobre como evoluir um grupo de pessoas que têm curiosidade sobre Go para criadores extremamente eficazes de sistemas em Go.

### O que não funcionou

#### Ler *o* livro

Uma abordagem que tentamos foi pegar [o livro azul](https://www.amazon.com.br/Linguagem-Programa%C3%A7%C3%A3o-Go-Alan-Donovan/dp/8575225464) e toda semana discutir um capítulo junto de exercícios.

Amo esse livro, mas ele exige muito comprometimento. O livro é bem detalhado na explicação de conceitos, o que obviamente é ótimo, mas significa que o progresso é lento e uniforme - não é para todo mundo.

Descobri que apenas um pequeno número de pessoas pegaria o capítulo X para ler e faria os exercícios, enquanto que a maioria não.

#### Resolver alguns problemas

Katas são divertidos, mas geralmente se limitam ao escopo de aprender uma linguagem; é improvável que você use goroutines para resolver um kata.

Outro problema é quando você tem níveis diferentes de entusiasmo. Algumas pessoas aprendem mais da linguagem que outras e, quando demonstram o que já fizeram, confundem essas pessoas apresentando funcionalidades que as outras ainda não conhecem.

Isso acaba tornando o aprendizado bem *desestruturado* e *específico*.

### O que funcionou

De longe, a forma mais eficaz foi apresentar os conceitos da linguagem aos poucos lendo o [go by example](https://gobyexample.com/), explorando-o com exemplos e discutindo-o como um grupo. Essa abordagem foi bem mais interativa do que "leia o capítulo X como lição de casa".

Com o tempo, a equipe ganhou uma base sólida da *gramática* da linguagem para que conseguíssemos começar a desenvolver sistemas.

Para mim, é semelhante à ideia de praticar escalas quando se tenta aprender a tocar violão.

Não importa quão artístico você seja; é improvável que você crie músicas boas sem entender os fundamentos e praticando os mecanismos.

### O que funcionou para mim

Quando *eu* aprendo uma nova linguagem de programação, costumo começar brincando em um REPL, mas hora ou outra preciso de mais estrutura.

O que eu gosto de fazer é explorar conceitos e então solidificar as ideias com testes. Testes certificam de que o código que escrevi está correto e documentam a funcionalidade que aprendi.

Usando minha experiência de aprendizado em grupo e a minha própria, vou tentar criar algo que seja útil para outras equipes. Aprender os conceitos escrevendo testes pequenos para que você possa usar suas habilidades de desenvolvimento de software e entregar sistemas ótimos.

## Para quem isso foi feito

* Pessoas que se interessam em aprender Go.
* Pessoas que já sabem Go, mas querem explorar testes com TDD.

## O que vamos precisar

* Um computador!
* [Go instalado](https://golang.org/)
* Um editor de texto
* Experiência com programação. Entendimento de conceitos como `if`, variáveis, funções etc.
* Se sentir confortável com o terminal

## Feedback

* Para a versão em português, submita um PR [aqui](https://github.com/larien/aprenda-go-com-testes) ou entre em contato comigo pelo [meu site](https://larien.dev).

[MIT license](https://github.com/larien/aprenda-go-com-testes/tree/09aafaeebaef4443e80a6216cc46fa3d7bfdabbb/LICENSE.md)

[Logo criado por egonelbre](https://github.com/egonelbre) Que estrela!


# Aprenda Go com Testes

![](/files/KGPsFrozFxmlxjWJwWHH)

[Arte por Denise](https://twitter.com/deniseyu21)

* Formatos: [Gitbook](https://larien.gitbook.io/aprenda-go-com-testes), [EPUB ou PDF](https://github.com/larien/aprenda-go-com-testes/releases)
* Versão original: [English](https://quii.gitbook.io/learn-go-with-tests/)

## Motivação

* Explore a linguagem Go escrevendo testes
* **Tenha uma base com TDD**. O Go é uma boa linguagem para aprender TDD por ser simples de aprender e ter testes nativamente
* Tenha confiança de que você será capaz de escrever sistemas robustos e bem testados em Go
* [Assista a um vídeo ou leia sobre o motivo pelo qual testes unitários e TDD são importantes](/aprenda-go-com-testes/main/meta/motivacao)

## Explicação

Tenho experiência em apresentar Go a equipes de desenvolvimento e tenho testado abordagens diferentes sobre como evoluir um grupo de pessoas que têm curiosidade sobre Go para criadores extremamente eficazes de sistemas em Go.

### O que não funcionou

#### Ler *o* livro

Uma abordagem que tentamos foi pegar [o livro azul](https://www.amazon.com.br/Linguagem-Programa%C3%A7%C3%A3o-Go-Alan-Donovan/dp/8575225464) e toda semana discutir um capítulo junto de exercícios.

Amo esse livro, mas ele exige muito comprometimento. O livro é bem detalhado na explicação de conceitos, o que obviamente é ótimo, mas significa que o progresso é lento e uniforme - não é para todo mundo.

Descobri que apenas um pequeno número de pessoas pegaria o capítulo X para ler e faria os exercícios, enquanto que a maioria não.

#### Resolver alguns problemas

Katas são divertidos, mas geralmente se limitam ao escopo de aprender uma linguagem; é improvável que você use goroutines para resolver um kata.

Outro problema é quando você tem níveis diferentes de entusiasmo. Algumas pessoas aprendem mais da linguagem que outras e, quando demonstram o que já fizeram, confundem essas pessoas apresentando funcionalidades que as outras ainda não conhecem.

Isso acaba tornando o aprendizado bem *desestruturado* e *específico*.

### O que funcionou

De longe, a forma mais eficaz foi apresentar os conceitos da linguagem aos poucos lendo o [go by example](https://gobyexample.com/), explorando-o com exemplos e discutindo-o como um grupo. Essa abordagem foi bem mais interativa do que "leia o capítulo X como lição de casa".

Com o tempo, a equipe ganhou uma base sólida da *gramática* da linguagem para que conseguíssemos começar a desenvolver sistemas.

Para mim, é semelhante à ideia de praticar escalas quando se tenta aprender a tocar violão.

Não importa quão artístico você seja; é improvável que você crie músicas boas sem entender os fundamentos e praticando os mecanismos.

### O que funcionou para mim

Quando *eu* aprendo uma nova linguagem de programação, costumo começar brincando em um REPL, mas hora ou outra preciso de mais estrutura.

O que eu gosto de fazer é explorar conceitos e então solidificar as ideias com testes. Testes certificam de que o código que escrevi está correto e documentam a funcionalidade que aprendi.

Usando minha experiência de aprendizado em grupo e a minha própria, vou tentar criar algo que seja útil para outras equipes. Aprender os conceitos escrevendo testes pequenos para que você possa usar suas habilidades de desenvolvimento de software e entregar sistemas ótimos.

## Para quem isso foi feito

* Pessoas que se interessam em aprender Go.
* Pessoas que já sabem Go, mas querem explorar testes com TDD.

## O que vamos precisar

* Um computador!
* [Go instalado](https://golang.org/)
* Um editor de texto
* Experiência com programação. Entendimento de conceitos como `if`, variáveis, funções etc.
* Se sentir confortável com o terminal

## Feedback

* Crie issues/submita PRs [aqui](https://github.com/quii/learn-go-with-tests) ou [me envie um tweet em @quii](https://twitter.com/quii).
* Para a versão em português, submita um PR [aqui](https://github.com/larien/aprenda-go-com-testes) ou entre em contato comigo pelo [meu site](https://larien.dev).

[MIT license](https://github.com/larien/aprenda-go-com-testes/tree/35d0dbb8c6467128a4b4637742b4c2fe563c0a88/LICENSE.md)

[Logo criado por egonelbre](https://github.com/egonelbre) Que estrela!


# Instalação do Go

As instruções oficiais de instalação do Go estão disponíveis [aqui](http://www.golangbr.org/doc/instalacao).

Esse guia vai presumir que você está usando um gerenciador de pacotes como [Homebrew](https://brew.sh), [Chocolatey](https://chocolatey.org), [Apt](https://help.ubuntu.com/community/AptGet/Howto) ou [yum](https://access.redhat.com/solutions/9934).

Para propósitos de demonstração, vamos te mostrar o procedimento de instalação para o OSX usando Homebrew.

## Instalação

### Mac OSX

O processo de instalação é bem simples. Primeiro, o que você precisa fazer é executar o comando abaixo pra instalar o homebrew (brew). O Brew depende do Xcode, então você deve se certificar de instalá-lo primeiro.

```bash
xcode-select --install
```

Depois, execute o comando a seguir para instalar o homebrew:

```bash
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

Agora você consegue instalar o Go:

```bash
brew install go
```

*Siga todas as instruções recomendadas pelo seu gerenciador de pacotes. **Nota** cada grupo de instruções varia de sistema operacional para sistema operacional.*

Você pode verificar a instalação com:

```bash
$ go version
go version go1.10 darwin/amd64
```

### Linux

O processo de instalação é bem simples. Primeiro você precisa escolher e baixar a versão do Go que você deseja instalar. Para isso [acesse o site oficial](https://golang.org/) da linguagem e copie o da versão desejada (Ex.: <https://dl.google.com/go/go1.13.linux-amd64.tar.gz>). Recomendamos instalar sempre a versão mais atual.

Para baixá-lo execute o seguinte comando no seu terminal.

```bash
# selecione a versão  que você deseja instalar, no nosso exemplo estamos utilizando a versão 1.13
VERSAO_GO=1.13
cd ~
curl -O "https://dl.google.com/go/go${VERSAO_GO}.linux-amd64.tar.gz"
```

Agora descompacte os arquivos com o seguinte comando.

```bash
tar xvf "go${VERSAO_GO}.linux-amd64.tar.gz"
```

E em seguida, mova os arquivos para o diretório de binário do seu usuário.

```bash
sudo mv go /usr/local
```

Agora teste a sua instalação.

```bash
go version
go version go1.13 linux/amd64
```

### Windows

Para usuários de Windows existem duas formas de instalação, através de um arquivo ZIP que requer que você configure algumas variáveis de ambiente ou uma arquivo MSI que faz toda a configuração automaticamente. Primeiro faça download da versão que você deseja instalar. Para isso [acesse o site oficial](https://golang.org/) da linguagem e copie o arquivo da versão desejada (Ex.: <https://dl.google.com/go/go1.13.1.windows-amd64.msi>). Recomendamos instalar sempre a versão mais atual.

#### Instalação via MSI

Abra o arquivo MSI e siga os passos da instalação. Por padrão o instalador adiciona o Go na pasta `C:\Go`.

O instalador adiciona o caminho `C:\Go\bin` na variável de ambiente "Path" e cria a variável de usuário "GOPATH" com o caminho `C:\Users\%USER%\go`.

#### Instalação via ZIP

Extraia os arquivos do arquivo ZIP no diretório de sua preferência.

Adicione na variável de ambiente "Path" o caminho para a pasta "bin" dos arquivos de Go. Na busca do menu Iniciar, digite "Variáveis" escolha a opção "Editar as variáveis de ambiente do sistema. Na aba "Avançado" clique em "Variáveis de Ambiente", localize a variável "Path" e clique em Editar > Novo e preencha com o caminho escolhido (Ex: `C:/minha-pasta-go/bin`).

Quando ocorre alteração nas variáveis de ambiente no Windows é necessário reiniciar o sistema.

Nos próximos passos vamos configurar o ambiente Go. As [instruções abaixo](#o-ambiente-go) valem tanto para sistema operacional OSX quanto para o Linux. O ambiente Windows pode requisitar configurações a mais e por isso é importante seguir a documentação oficial.

## O Ambiente Go

O Go divide opiniões.

Por convenção, todo o código Go é colocado dentro de apenas um workspace (pasta). Esse workspace pode estar em qualquer lugar da sua máquina. Se você não especificar, o Go vai definir o $HOME/go como workspace padrão. Ele é identificado (e modificado) pela variável de ambiente [GOPATH](https://golang.org/cmd/go/#hdr-GOPATH_environment_variable).

Você precisa definir a variável de ambiente para que possa utilizar futuramente em scripts, shells etc.

Atualize seu .bash\_profile para conter os seguintes `exports`:

```bash
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
```

*Nota* você deve abrir um novo terminal para definir essas variáveis de ambiente.

O Go presume que seu workspace contenha uma estrutura de diretórios específica.

Ele coloca seus arquivos em três diretórios: todo o código-fonte fica em `src`, os objetos dos pacotes ficam em `pkg` e os programas compilados são colocados em `bin`. É possível criar esses diretórios com o comando a seguir:

```bash
mkdir -p $GOPATH/src $GOPATH/pkg $GOPATH/bin
```

Agora você é capaz de usar o *go get* para que o `src/package/bin` seja instalado corretamente no diretório $GOPATH/xxx apropriado.

## Editor Go

A escolha de editor é bem pessoal. Você pode já ter um de sua preferência que tem suporte a Go. Se não tiver, leve em consideração um Editor como o [Visual Studio Code](https://code.visualstudio.com), que tem um suporte exceptional à linguagem.

Você pode instalá-lo com o comando a seguir:

```bash
brew install --cask visual-studio-code
```

Confirme que o VS Code foi instalado corretamente executando o seguinte comando:

```bash
code .
```

O VS Code é lançado com poucos softwares habilidados. Você pode habilitar novos softwares instalando extensões. Para adicionar o suporte a Go, você deve instalar uma extensão. Existem várias disponíveis para o VS Code, mas uma excepcional é a do [Luke Hoban](https://github.com/Microsoft/vscode-go). Instale-a da forma a seguir:

```bash
code --install-extension ms-vscode.go
```

Quando abrir um arquivo Go pela primeira vez no VS Code, ele vai indicar que ferramentas de análises estão faltando. Clique no botão para instalá-las. A lista de ferramentas que são instaladas (e usadas) pelo VS Code estão disponíveis [aqui](https://github.com/Microsoft/vscode-go/wiki/Go-tools-that-the-Go-extension-depends-on).

## Debugger do Go

Uma boa opção para debugar seus programas em Go (que é integrado com o VS Code) é o Delve. Ele pode ser instalado da seguinte maneira usando `go get`:

```bash
go get -u github.com/go-delve/delve/cmd/dlv
```

## Linter do Go

Uma melhoria sob o linter padrão pode ser configurada usando o [GolangCI-Lint](https://github.com/golangci/golangci-lint).

Que pode ser instalada da seguinte forma:

```bash
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
```

## Refatoração e suas ferramentas

Uma grande ênfase nesse livro é dada na importância da refatoração.

Suas ferramentas podem te ajudar a fazer uma refatoração com maior confiança.

Você deve ter familiaridade o suficiente com seu editor para performar as ações a seguir com uma simples combinação de teclas:

* **Extrair/alinhar variável**. Ser capaz de pegar valores mágicos e dar um nome a eles vai simplificar seu código rapidamente.
* **Extrair método/função**. É crucial ser capaz de tirar uma seção do código e extrair funções/métodos.
* **Renomear**. Você deve se sentir capaz de renomear símbolos no decorrer dos arquivos com confiança.
* **go fmt**. O Go tem um formatador nativo chamado `go fmt`. Seu editor deve executar esse comando a cada vez que salvar o arquivo.
* **Executar testes**. Não precisa nem dizer que você deve ser capaz de fazer todos os pontos acima e então re-executar seus testes rapidamente para certificar que sua refatoração não quebrou nada.

Além disso, para te ajudar a trabalhar com seu código, você deve ser capaz de:

* **Verificar a assinatura da função**. Nunca tenha dúvida sobre a forma de chamar uma função em Go. Sua IDE deve descrever uma função em termos de sua documentação, seus parâmetros e o que ela retorna.
* **Ver a definição da função**. Se não tiver certeza sobre como uma função funciona, você deve ser capaz de ir para o código fonte de descobrir por si facilmente.
* **Encontrar usos de um símbolo**. Ser capaz de ver o contexto de uma função sendo chamada pode te ajudar com o processo de refatoração.

Dominar suas ferramentas vai te ajudar a concentrar no código e reduzir a troca de contexto.

## Resumindo

Nesse ponto você já deve ter o Go instalado, um editor disponível e algumas ferramentas básicas configuradas. O Go tem um ecossistema enorme de produtos feitos por outras pessoas. Identificamos alguns componentes úteis aqui, mas você pode encontrar uma lista mais completa no [Awesome Go](https://awesome-go.com).


# Olá, mundo

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/ola-mundo)

É comum o primeiro programa em uma nova linguagem ser um *Olá, mundo*.

* Crie uma pasta onde quiser
* Dentro da pasta, crie um arquivo chamado `ola.go` e coloque o seguinte código dentro dele

```go
package main

import "fmt"

func main() {
    fmt.Println("Olá, mundo")
}
```

Para executá-lo, `go run ola.go`

## Como isso funciona?

Quando você escreve um programa em Go, há um pacote `main` definido com uma função(`func`) `main` (principal) dentro dele. Os pacotes são maneiras de agrupar códigos Go relacionados.

A palavra reservada `func` é utilizada para que você defina uma função com um nome e um conteúdo.

Ao usar `import "fmt"`, estamos importando um pacote que contém a função `Println` que será utilizada para imprimir (escrever) um valor na tela.

## Como testar isso?

Como você testaria isso? É bom separar seu "domínio"(suas regras de negócio) do resto do mundo (efeitos colaterais). A função `fmt.Println` é um efeito colateral (que está imprimindo um valor no ***stdout** \[saída padrão do terminal]*) e a string que estamos enviando para dentro dela é nosso domínio.

Então, vamos separar essas referências para ficar mais fácil para testarmos.

```go
package main

import "fmt"

func Ola() string {
    return "Olá, mundo"
}

func main() {
    fmt.Println(Ola())
}
```

Criamos uma nova função usando `func`, mas dessa vez adicionamos outra palavra reservada `string` na sua definição. Isso significa que essa função terá como retorno uma `string` (*cadeia de caracteres*).

Agora, criaremos outro arquivo chamado `ola_test.go` onde iremos escrever um teste para a nossa função `Ola`.

```go
package main

import "testing"

func TestOla(t *testing.T) {
	resultado := Ola()
	esperado := "Olá, mundo"

	if resultado != esperado {
		t.Errorf("resultado %q, esperado %q", resultado, esperado)
	}
}
```

## Módulos Go?

A próxima etapa é executar os testes. Digite `go test` em seu terminal. Se os testes passarem, provavelmente você está usando uma versão anterior do Go. No entanto, se você estiver usando Go 1.16 ou posterior, os testes provavelmente não serão executados. Em vez disso, você verá uma mensagem de erro como esta no terminal:

Qual é o problema? Em uma palavra, [módulos](https://blog.golang.org/go116-module-changes). Felizmente, o problema é fácil de resolver. Digite `go mod init ola` em seu terminal. Isso criará um novo arquivo com o seguinte conteúdo:

```go
module ola

go 1.16 // a versão pode mudar dependendo da que você tem instalado na sua máquina
```

Este arquivo fornece às ferramentas `go` informações essenciais sobre o seu código. Se você planeja distribuir sua aplicação, você incluiria onde o código está disponível para download, bem como informações sobre dependências. Por enquanto, seu arquivo de módulo é mínimo e você pode deixá-lo assim. Para ler mais sobre os módulos, [você pode verificar a referência na documentação do Golang](https://golang.org/doc/modules/gomod-ref). Podemos voltar a testar e aprender o Go agora, pois os testes devem ser executados, mesmo no Go 1.16.

Em capítulos futuros, você precisará executar `go mod init ALGUM_NOME` em cada nova pasta antes de executar comandos como `go test` ou `go build`.

## De volta ao teste

Execute `go test` em seu terminal. Deve ter passado! Apenas para verificar, tente quebrar o teste, alterando a string `esperado`.

Observe como você não teve que escolher entre vários frameworks de teste e, em seguida, descobrir como instalar. Tudo o que você precisa está embutido na linguagem e a sintaxe é a mesma do resto do código que você escreverá.

### Escrevendo testes

Escrever um teste é como escrever uma função, com algumas regras:

* Precisa estar em um arquivo com um nome parecido com `xxx_test.go`
* A função de teste precisa começar com a palavra `Test`
* A função de teste recebe um único argumento, que é `t *testing.T`
* Para usar o tipo `*testing.T`, você precisa importar `"testing"`, como fizemos com `fmt` no outro arquivo

Por enquanto é o bastante para saber que o nosso `t` do tipo `*testing.T` é a nossa porta de entrada para a ferramenta de testes e assim você poderá utilizar o `t.Fail()` quando precisar relatar um erro.

Abordando alguns novos tópicos:

#### `if`

Instruções `if` em Go são muito parecidas com as de outras linguagens.

#### Declarando variáveis

Estamos declarando algumas variáveis com a sintaxe `nomeDaVariavel := valor`, que nos permite reutilizar alguns valores nos nossos testes de maneira legível.

#### `t.Errorf`

Estamos chamando o *método* `Errorf` em nosso `t` que irá imprimir uma mensagem e falhar o teste. O sufixo `f` no final de `Errorf` representa que podemos formatar e montar uma string com valores inseridos dentro de valores de preenchimentos `%s`. Quando fazemos um teste falhar, devemos ser bastante claros com o que aconteceu.

Iremos explorar a diferença entre métodos e funções depois.

### Go doc

Outra funcionalidade importante do Go é sua documentação. Você pode ver a documentação na sua máquina rodando `godoc -http :8000`. Se acessar [localhost:8000/pkg](http://localhost:8000/pkg) no seu navegador, verá todos os pacotes instalados no seu sistema.

A vasta biblioteca padrão da linguagem tem uma documentação excelente com exemplos. Deve valer a pena dar uma olhada em <http://localhost:8000/pkg/testing/> para verificar o que está disponível para você.

Se você não tiver o comando `godoc`, talvez esteja usando uma versão mais recente do Go (1.14 ou posterior), [que não inclui mais o `godoc`](https://golang.org/doc/go1.14#godoc). Você pode instalá-lo manualmente com `go install golang.org/x/tools/cmd/godoc`.

**nota**: Se você tiver usando Go 1.17, ou superior, você deve instalar o godoc com `go install golang.org/x/tools/cmd/godoc`, pois o comando `go get` para instalar executáveis ficou [obsoleto](https://golang.org/doc/go-get-install-deprecation).

### Olá, VOCÊ

Agora que temos um teste, podemos iterar sobre nosso software de maneira segura.

No último exemplo, escrevemos o teste somente *depois* do código ser escrito apenas para que você pudesse ter um exemplo de como escrever um teste e declarar uma função. A partir de agora, *escreveremos os testes primeiro*.

Nosso próximo requisito é nos deixar especificar quem recebe a saudação.

Vamos começar especificando esses requisitos em um teste. Estamos praticando TDD (Desenvolvimento Orientado a Testes) de forma bastante simples e que nos permite ter certeza que nosso teste está *testando* o que precisamos. Quando você escreve testes retroativamente existe o risco que seu teste possa continuar passando mesmo que o código não esteja funcionando como esperado.

```go
package main

import "testing"

func TestOla(t *testing.T) {
	resultado := Ola("Chris")
	esperado := "Olá, Chris"

	if resultado != esperado {
		t.Errorf("resultado %q, esperado %q", resultado, esperado)
	}
}
```

Agora, rodando `go test`, deve ter aparecido um erro de compilação:

```
./ola_test.go:6:18: too many arguments in call to Ola
    have (string)
    want ()
```

```
./ola_test.go:6:18: argumentos demais na chamada para Ola
    tem (string)
    quer ()
```

Quando estiver usando uma linguagem estaticamente tipada como Go, é importante *dar atenção ao compilador*. O compilador entende como seu código deve se encaixar, não delegando essa função para você.

Neste caso, o compilador está te falando o que você precisa fazer para continuar. Temos que mudar a nossa função `Ola` para receber um argumento.

Edite a função `Ola` para que um argumento do tipo string seja aceito:

```go
func Ola(nome string) string {
    return "Olá, mundo"
}
```

Se tentar rodar seus testes novamente, seu arquivo `ola.go` irá falhar durante a compilação porque você não está passando um argumento. Passe "mundo" como argumento para fazer o teste passar.

```go
func main() {
    fmt.Println(Ola("mundo"))
}
```

Agora, quando for rodar seus testes, você verá algo parecido com isso:

```
ola_test.go:10: resultado 'Olá, mundo', esperado 'Olá, Chris'
```

Finalmente temos um programa que compila, mas que não está satisfazendo os requisitos de acordo com o teste.

Vamos, então, fazer o teste passar usando o argumento `nome` e concatenar com `Olá,`

```go
func Ola(nome string) string {
    return "Olá, " + nome
}
```

Quando você rodar os testes, eles irão passar. É comum como parte do ciclo do TDD *refatorar* o nosso código agora.

### Uma nota sobre versionamento de código

Nesse ponto, se você estiver usando um versionamento de código (que você deveria estar fazendo!) eu faria um `commit` do código no estado atual. Agora, temos um software funcional suportado por um teste.

No entanto, eu *não faria* um push para a branch principal, pois planejo refatorar em breve. É legal fazer um commit nesse ponto porque você pode se perder com a refatoração. Fazendo um commit você pode sempre voltar para a última versão funcional do seu software.

Não tem muita coisa para refatorar aqui, mas podemos introduzir outro recurso da linguagem: *constantes*.

### Constantes

Constantes podem ser definidas como o exemplo abaixo:

```go
const prefixoOlaPortugues = "Olá, "
```

Agora, podemos refatorar nosso código:

```go
const prefixoOlaPortugues = "Olá, "

func Ola(nome string) string {
    return prefixoOlaPortugues + nome
}
```

Depois da refatoração, rode novamente os seus testes para ter certeza que você não quebrou nada.

Constantes devem melhorar a performance da nossa aplicação, assim como evitar que você crie uma string `"Ola, "` para cada vez que `Ola` é chamado.

Para esclarecer, o aumento de performance é incrivelmente insignificante para esse exemplo! Mas vale a pena pensar em criar constantes para capturar o significado dos valores e, às vezes, para ajudar no desempenho.

## Olá, mundo... novamente

O próximo requisito é: quando nossa função for chamada com uma string vazia, ela precisa imprimir o valor padrão "Olá, mundo", ao invés de "Olá, ".

Começaremos escrevendo um novo teste que irá falhar

```go
func TestOla(t *testing.T) {

    t.Run("diz olá para as pessoas", func(t *testing.T) {
        resultado := Ola("Chris")
        esperado := "Olá, Chris"

        if resultado != esperado {
            t.Errorf("resultado %q, esperado %q", resultado, esperado)
        }
    })

    t.Run("diz 'Olá, mundo' quando uma string vazia for passada", func(t *testing.T) {
        resultado := Ola("")
        esperado := "Olá, mundo"

        if resultado != esperado {
            t.Errorf("resultado %q, esperado %q", resultado, esperado)
        }
    })
}
```

Aqui nós estamos apresentando outra ferramenta em nosso arsenal de testes, os *subtestes*. Às vezes, é útil agrupar testes em torno de uma "coisa" e, em seguida, ter *subtestes* descrevendo diferentes cenários.

O benefício dessa abordagem é que você poderá construir um código que pode ser compartilhado por outros testes.

Há um código repetido quando verificamos se a mensagem é o que esperamos.

A refatoração não vale *apenas* para o código de produção!

É importante que seus testes *sejam especificações claras* do que o código precisa fazer.

Podemos e devemos refatorar nossos testes.

```go
func TestOla(t *testing.T) {
    verificaMensagemCorreta := func(t testing.TB, resultado, esperado string) {
        t.Helper()
        if resultado != esperado {
            t.Errorf("resultado %q, esperado %q", resultado, esperado)
        }
    }

    t.Run("diz olá para as pessoas", func(t *testing.T) {
        resultado := Ola("Chris")
        esperado := "Olá, Chris"
        verificaMensagemCorreta(t, resultado, esperado)
    })

    t.Run("'Mundo' como padrão para string vazia", func(t *testing.T) {
        resultado := Ola("")
        esperado := "Olá, Mundo"
        verificaMensagemCorreta(t, resultado, esperado)
    })
}
```

### O que fizemos aqui?

Refatoramos nossa asserção em uma função. Isso reduz a duplicação e melhora a legibilidade de nossos testes. No Go, você pode declarar funções dentro de outras funções e atribui-las a variáveis. Você pode chamá-las, assim como as funções normais. Precisamos passar `t *testing.T` como parâmetro para que possamos dizer ao código de teste que ele falhará quando necessário.

`t.Helper()` é necessário para dizermos ao conjunto de testes que este é um método auxiliar. Ao fazer isso, quando o teste falhar, o número da linha relatada estará em nossa chamada de função, e não dentro do nosso auxiliar de teste. Isso ajudará outros desenvolvedores a rastrear os problemas com maior facilidade. Se você ainda não entendeu, comente, faça um teste falhar e observe a saída do teste.

Agora que temos um teste bem escrito falhando, vamos corrigir o código usando um `if`.

```go
const prefixoOlaPortugues = "Olá, "

func Ola(nome string) string {
    if nome == "" {
        nome = "Mundo"
    }
    return prefixoOlaPortugues + nome
}
```

Se executarmos nossos testes, veremos que ele satisfaz o novo requisito e não quebramos acidentalmente a outra funcionalidade.

### De volta ao controle de versão

Agora estamos felizes com o código. Eu adicionaria mais um commit ao anterior para que possamos verificar o quão adorável ficou o nosso código com os testes.

### Disciplina

Vamos repassar o ciclo novamente:

* Escrever um teste
* Compilar o código sem erros
* Rodar o teste, ver o teste falhar e certificar que a mensagem de erro faz sentido
* Escrever a quantidade mínima de código para o teste passar
* Refatorar

Este ciclo pode parecer tedioso, mas se manter nesse ciclo de feedback é importante.

Ele não apenas garante que você tenha *testes relevantes*, como também ajuda a *projetar um bom software* refatorando-o com a segurança dos testes.

Ver a falha no teste é uma verificação importante porque também permite que você veja como é a mensagem de erro. Para quem programa, pode ser muito difícil trabalhar com uma base de código que, quando há falha nos testes, não dá uma ideia clara de qual é o problema.

Assegurando que seus testes sejam rápidos e configurando suas ferramentas para que a execução de testes seja simples, você pode entrar em um estado de fluxo ao escrever seu código.

Ao não escrever testes, você está comprometendo-se a verificar manualmente seu código executando o software que interrompe seu estado de fluxo, o que não economiza tempo, especialmente a longo prazo.

## Continue! Mais requisitos

Caramba, temos mais requisitos. Agora precisamos suportar um segundo parâmetro, especificando o idioma da saudação. Se for passado um idioma que não reconhecemos, use como padrão o português.

Devemos ter certeza de que podemos usar o TDD para aprimorar essa funcionalidade facilmente!

Escreva um teste para um usuário, passando espanhol. Adicione-o ao conjunto de testes existente.

```go
    t.Run("em espanhol", func(t *testing.T) {
        resultado := Ola("Elodie", "espanhol")
        esperado := "Hola, Elodie"
        verificaMensagemCorreta(t, resultado, esperado)
    })
```

Lembre-se de não trapacear! *Primeiro os testes*. Quando você tenta executar o teste, o compilador deve reclamar porque está chamando `Ola` com dois argumentos ao invés de um.

```
./ola_test.go:27:19: too many arguments in call to Ola
    have (string, string)
    want (string)
```

Acerte os problemas de compilação, adicionando um novo argumento do tipo `string` ao método `Ola`:

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }
    return prefixoOlaPortugues + nome
}
```

Quando você tentar executar o teste novamente, ele se queixará da função não ter recebido argumentos o suficiente para `Ola` nos seus outros testes em `ola.go`:

```
./ola.go:15:19: not enough arguments in call to Ola
    have (string)
    want (string, string)
```

Corrija-os passando `strings` vazia. Agora todos os seus testes devem compilar *e* passar, além do nosso novo cenário:

```
ola_test.go:29: resultado 'Olá, Elodie', esperado 'Hola, Elodie'
```

Podemos usar `if` aqui para verificar se o idioma é igual a "espanhol" e, em caso afirmativo, alterar a mensagem:

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    if idioma == "espanhol" {
        return "Hola, " + nome
    }

    return prefixoOlaPortugues + nome
}
```

Os testes devem passar agora.

Agora é hora de *refatorar*. Você verá alguns problemas no código, sequências de caracteres "mágicas", algumas das quais são repetidas. Tente refatorar você mesmo, a cada alteração, execute novamente os testes para garantir que sua refatoração não esteja quebrando nada.

```go
const espanhol = "espanhol"
const prefixoOlaPortugues = "Olá, "
const prefixoOlaEspanhol = "Hola, "

func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    if idioma == espanhol {
        return prefixoOlaEspanhol + nome
    }

    return prefixoOlaPortugues + nome
}
```

### Francês

* Escreva um teste que verifique que quando passamos o idioma `"francês"`, obtemos `"Bonjour, "`
* Veja o teste falhar, verifique se a mensagem de erro é fácil de ler
* Faça a mínima alteração de código o suficiente para que o teste passe

Você pode ter escrito algo parecido com isso:

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    if idioma == espanhol {
        return prefixoOlaEspanhol + nome
    }

    if idioma == frances {
        return prefixoOlaFrances + nome
    }

    return prefixoOlaPortugues + nome
}
```

## `switch`

Quando você tem muitas instruções `if` verificando um valor específico, é comum usar uma instrução `switch`. Podemos usar o `switch` para refatorar o código facilitando a leitura e a sua extensão, caso desejarmos adicionar suporte a mais idiomas posteriormente.

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    prefixo := prefixoOlaPortugues

    switch idioma {
    case frances:
        prefixo = prefixoOlaFrances
    case espanhol:
        prefixo = prefixoOlaEspanhol
    }

    return prefixo + nome
}
```

Faça um teste para incluir agora uma saudação no idioma de sua escolha e você deve ver como é simples estender nossa *fantástica* função.

### Uma...última...refatoração?

Você pode achar que talvez nossa função esteja ficando um pouco grande. A refatoração mais simples para isso seria extrair algumas funcionalidades para outra função.

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    return prefixodeSaudacao(idioma) + nome
}

func prefixodeSaudacao(idioma string) (prefixo string) {
    switch idioma {
    case frances:
        prefixo = prefixoOlaFrances
    case espanhol:
        prefixo = prefixoOlaEspanhol
    default:
        prefixo = prefixoOlaPortugues
    }
    return
}
```

Alguns novos conceitos:

* Em nossa assinatura de função, criamos um valor de retorno chamado `(prefixo string)`.
* Isso criará uma variável chamada `prefixo` na nossa função.
  * Lhe será atribuído o valor "zero". Isso dependendo do tipo, por exemplo, para `int` será `0` e para strings será `""`.
    * Você pode retornar o que quer que esteja definido, apenas chamando `return` ao invés de `return prefixo`.
  * Isso será exibido no `go doc` para sua função, para que possa tornar a intenção do seu código mais clara.
* `default` será escolhido caso o valor recebido não corresponda a nenhuma das outras instruções `case` do `switch`.
* O nome da função começa com uma letra minúscula. As funções públicas em *Go* começam com uma letra maiúscula e as privadas, com minúsculas. Não queremos que as partes internas do nosso algoritmo sejam expostas ao mundo, portanto tornamos essa função privada.

## Resumindo

Quem imaginaria que você poderia tirar tanto proveito de um `Olá, mundo`?

Até agora você deve ter alguma compreensão de:

### Algumas das sintaxes da linguagem *Go* para:

* Escrever testes
* Declarar funções, com argumentos e tipos de retorno
* `if`, `const` e `switch`
* Declarar variáveis e constantes

### O processo TDD e *por que* as etapas são importantes

* *Escreva um teste que falhe e veja-o falhar*, para que saibamos que escrevemos um teste *relevante* para nossos requisitos e vimos que ele produz uma *descrição da falha fácil de entender*
* Escrever a menor quantidade de código para fazer o teste passar, para que saibamos que temos um software funcionando
* *Em seguida*, refatorar, tendo a segurança de nossos testes para garantir que tenhamos um código bem feito e fácil de trabalhar

No nosso caso, passamos de `Ola()` para `Ola("nome")`, para `Ola ("nome"," Francês ")` em etapas pequenas e fáceis de entender.

Naturalmente, isso é trivial comparado ao software do "mundo real", mas os princípios ainda permanecem. O TDD é uma habilidade que precisa de prática para se desenvolver. No entanto, você terá muito mais facilidade em escrever software sendo capaz de dividir os problemas em pedaços menores que possa testar.


# Inteiros

[**Você pode encontrar todos os códigos desse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/inteiros)

Inteiros funcionam como é de se esperar. Vamos escrever uma função de soma para testar algumas coisas. Crie um arquivo de teste chamado `adicionador_test.go` e escreva o seguinte código.

**nota**: Os arquivos-fonte de Go devem ter apenas um `package`(pacote) por diretório, verifique se os arquivos estão organizados separadamente. [Aqui tem uma boa explicação sobre isso (em inglês).](https://dave.cheney.net/2014/12/01/five-suggestions-for-setting-up-a-go-project)

## Escreva o teste primeiro

```go
package inteiros

import "testing"

func TestAdicionador(t *testing.T) {
    soma := Adiciona(2, 2)
    esperado := 4

    if soma != esperado {
        t.Errorf("esperado '%d', resultado '%d'", esperado, soma)
    }
}
```

Você deve ter notado que estamos usando `%d` como string de formatação, em vez de `%s`. Isso porque queremos que ele imprima um valor inteiro e não uma string. Observe também que não estamos mais usando o pacote `main`, em vez disso, definimos um pacote chamado `inteiros`, pois o nome sugere que ele agrupará funções para trabalhar com números inteiros, como Adiciona.

## Tente executar o teste

Execute o test com `go test`

Inspecione o erro de compilação

`./adicionador_test.go:6:9: undefined: Adiciona`

## Escreva a quantidade mínima de código para o teste rodar e verifique o erro na saída do teste

Escreva apenas o suficiente de código para satisfazer o compilador *e nada mais* - lembre-se de que queremos verificar se nossos testes falham pelo motivo certo.

```go
package inteiros

func Adiciona(x, y int) int {
    return 0
}
```

Quando você tem mais de um argumento do mesmo tipo (no nosso caso dois inteiros) ao invés de ter `(x int, y int)` você pode encurtá-lo para `(x, y int)`.

Agora execute os testes. Devemos ficar felizes que o teste esteja relatando corretamente o que está errado.

`adicionador_test.go:10: esperado '4', resultado '0'`

Você deve ter percebido que aprendemos sobre o *valor de retorno nomeado* na [última](https://larien.gitbook.io/aprenda-go-com-testes/main/primeiros-passos-com-go/pages/-MQPKd9XwFetEOmPOvz1#uma...ultima...refatoracao?) seção, mas não estamos usando aqui. Ele geralmente deve ser usado quando o significado do resultado não está claro no contexto. No nosso caso, é muito claro que a função `Adiciona` irá adicionar os parâmetros. Você pode consultar [esta](https://github.com/golang/go/wiki/CodeReviewComments#named-result-parameters) wiki para mais detalhes.

## Escreva código o suficiente para fazer o teste passar

No sentido estrito de TDD, devemos escrever a *quantidade mínima de código para fazer o teste passar*. Uma pessoa pretenciosa pode fazer isso:

```go
func Adiciona(x, y int) int {
    return 4
}
```

Ah hah! Frustração mais uma vez! TDD é uma farsa, né?

Poderíamos escrever outro teste, com números diferentes para forçar o teste a falhar, mas isso parece um jogo de gato e rato.

Quando estivermos mais familiarizados com a sintaxe do Go, apresentarei uma técnica chamada Testes Baseados em Propriedade, que interromperá a irritação das pessoas e ajudará a encontrar bugs.

Por enquanto, vamos corrigi-lo corretamente:

```go
func Adiciona(x, y int) int {
    return x + y
}
```

Se você executar os testes novamente, eles devem passar.

## Refatoração

Não há muitas melhorias que possamos fazer aqui.

Anteriormente, vimos como nomear o argumento de retorno que aparece na documentação e também na maioria dos editores de código.

Isso é ótimo porque ajuda na usabilidade do código que você está escrevendo. É preferível que um usuário possa entender o uso de seu código apenas observando a assinatura de tipo e a documentação.

Você pode adicionar documentação em funções escrevendo comentários, e elas aparecerão no Go Doc como quando você olha a documentação da biblioteca padrão.

```go
// Adiciona recebe dois inteiros e retorna a soma deles
func Adiciona(x, y int) int {
    return x + y
}
```

### Exemplos

Se realmente quer ir além, você pode fazer [exemplos](https://blog.golang.org/examples). Você encontrará muitos exemplos na documentação da biblioteca padrão.

Muitas vezes, exemplos encontrados fora da base de código, como um arquivo readme, ficam desatualizados e incorretos em comparação com o código real, porque eles não são verificados.

Os exemplos de Go são executados da mesma forma que os testes, para que você possa ter certeza de que eles refletem o que o código realmente faz.

Exemplos são compilados (e opcionalmente executados) como parte do conjunto de testes de um pacote.

Como nos testes comuns, os exemplos são funções que residem nos arquivos \_test.go de um pacote. Adicione a seguinte função ExampleAdiciona no arquivo `adicionador_test.go`.

```go
func ExampleAdiciona() {
    soma := Adiciona(1, 5)
    fmt.Println(soma)
    // Output: 6
}
```

> obs: As palavras Example e Output foram mantidas em inglês para a execução correta do código.

(Se o seu editor não importar os pacotes automaticamente, a etapa de compilação irá falhar porque você não colocou o `import "fmt"` no `adicionador_test.go`. É altamente recomendável que você pesquise como ter esses tipos de erros corrigidos automaticamente em qualquer editor que esteja usando.)

Se o seu código mudar fazendo com que o exemplo não seja mais válido, você vai ter um erro de compilação.

Executando os testes do pacote, podemos ver que a função de exemplo é executada sem a necessidade de ajustes:

```bash
$ go test -v
=== RUN   TestAdicionador
--- PASS: TestAdicionador (0.00s)
=== RUN   ExampleAdiciona
--- PASS: ExampleAdiciona (0.00s)
```

Note que a função de exemplo não será executada se você remover o comentário "// Output: 6". Embora a função seja compilada, ela não será executada.

Ao adicionar este trecho de código, o exemplo aparecerá na documentação dentro do `godoc`, tornando seu código ainda mais acessível.

Para ver como isso funciona, execute `godoc -http=:6060` e navegue para `http://localhost:6060/pkg/`

Aqui você vai ver uma lista de todos os pacotes em seu `$GOPATH`. Então, supondo que tenha escrito esse código em algum lugar como `$GOPATH/src/github.com/{seu_id}`, você poderá encontrar uma documentação com seus exemplos.

Se você publicar seu código com exemplos em uma URL pública, poderá compartilhar a documentação em [godoc.org](https://godoc.org). Por exemplo, aqui está a API finalizada deste capítulo <https://godoc.org/github.com/larien/aprenda-go-com-testes/inteiros/v2>.

## Resumindo

Falamos sobre:

* Mais práticas do fluxo de trabalho de TDD
* Inteiros, adição
* Escrever melhores documentações para que os usuários do nosso código possam entender seu uso rapidamente
* Exemplos de como usar nosso código, que são verificados como parte de nossos testes


# Iteração

[**Você pode encontrar todo o código desse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/iteracao)

Para fazer coisas repetidamente em Go, você precisará do `for`. Go não possui nenhuma palavra chave do tipo `while`, `do` ou `until`. Você pode usar apenas `for`, o que é uma coisa boa!

Vamos escrever um teste para uma função que repete um caractere 5 vezes.

Não há nenhuma novidade até aqui, então tente escrever você mesmo para praticar.

## Escreva o teste primeiro

```go
package iteracao

import "testing"

func TestRepetir(t *testing.T) {
    repeticoes := Repetir("a")
    esperado := "aaaaa"

    if repeticoes != esperado {
        t.Errorf("esperado '%s' mas obteve '%s'", esperado, repeticoes)
    }
}
```

## Execute o teste

`./repetir_test.go:6:14: undefined: Repetir`

## Escreva a quantidade mínima de código para o teste rodar e verifique o erro na saída

*Mantenha a disciplina!* Você não precisa saber nada de diferente agora para fazer o teste falhar apropriadamente.

Tudo o que foi feito até agora é o suficiente para compilar, para que você possa verificar se escreveu o teste corretamente.

```go
package iteracao

func Repetir(caractere string) string {
    return ""
}
```

Não é legal saber que você já conhece o bastante em Go para escrever testes para problemas simples? Isso significa que agora você pode mexer no código de produção o quanto quiser sabendo que ele se comportará da maneira que você desejar.

`repetir_test.go:10: esperado 'aaaaa' mas obteve ''`

## Escreva código o suficiente para fazer o teste passar

A sintaxe do `for` é muito fácil de lembrar e segue a maioria das linguagens baseadas em `C`:

```go
func Repetir(caractere string) string {
    var repeticoes string
    for i := 0; i < 5; i++ {
        repeticoes = repeticoes + caractere
    }
    return repeticoes
}
```

Ao contrário de outras linguagens como `C`, `Java` ou `Javascript`, não há parênteses ao redor dos três componentes do `for`. No entanto, as chaves `{ }` são obrigatórias.

Execute o teste e ele deverá passar.

Variações adicionais do loop `for` podem ser vistas [aqui](https://gobyexample.com/for).

## Refatoração

Agora é hora de refatorarmos e apresentarmos outro operador de atribuição: o `+=`.

```go
const quantidadeRepeticoes = 5

func Repetir(caractere string) string {
    var repeticoes string
    for i := 0; i < quantidadeRepeticoes; i++ {
        repeticoes += caractere
    }
    return repeticoes
}
```

O operador adicionar & atribuir `+=` adiciona o valor que está à direita no valor que esta à esquerda e atribui o resultado ao valor da esquerda. Também funciona com outros tipos, como por exemplo, inteiros (`integer`).

### Benchmarking

Escrever [benchmarks](https://golang.org/pkg/testing/#hdr-Benchmarks) em Go é outro recurso disponível nativamente na linguagem e é tão facil quanto escrever testes.

```go
func BenchmarkRepetir(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Repetir("a")
    }
}
```

Você notará que o código é muito parecido com um teste.

O `testing.B` dará a você acesso a `b.N`.

Quando o benchmark é rodado, ele executa `b.N` vezes e mede quanto tempo leva.

A quantidade de vezes que o código é executado não deve importar para você. O framework irá determinar qual valor é "bom" para que você consiga ter resultados decentes.

Para executar o benchmark, digite `go test -bench=.` no terminal (ou se estiver executando do PowerShell do Windows, `go test-bench="."`)

```bash
goos: darwin
goarch: amd64
pkg: github.com/larien/aprenda-go-com-testes/primeiros-passos-com-go/iteracao/v4
10000000           136 ns/op
PASS
```

`136 ns/op` significa que nossa função demora cerca de 136 nanossegundos para ser executada (no meu computador). E isso é ótimo! Para chegar a esse resultado ela foi executada 10000000 (10 milhões de vezes) vezes.

*NOTA* por padrão, o benchmark é executado sequencialmente.

## Exercícios para praticar

* Altere o teste para que a função possa especificar quantas vezes o caractere deve ser repetido e então corrija o código para passar no teste.
* Escreva `ExampleRepetir` para documentar sua função.
* Veja também o pacote [strings](https://golang.org/pkg/strings). Encontre funções que você considera serem úteis e experimente-as escrevendo testes como fizemos aqui. Investir tempo aprendendo a biblioteca padrão irá te recompensar com o tempo.

## Resumindo

* Mais praticás de TDD
* Aprendemos o `for`
* Aprendemos como escrever benchmarks


# Arrays e slices

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/arrays-e-slices)

Arrays te permitem armazenar diversos elementos do mesmo tipo em uma variável em uma ordem específica.

Quando você tem um array, é muito comum ter que percorrer sobre ele. Logo, vamos usar nosso [recém adquirido conhecimento de `for`](/aprenda-go-com-testes/main/primeiros-passos-com-go/iteracao) para criar uma função `Soma`. `Soma` vai receber um array de números e retornar o total.

Também vamos praticar nossas habilidades em TDD.

## Escreva o teste primeiro

Em `soma_test.go`:

```go
package main

import "testing"

func TestSoma(t *testing.T) {

    numeros := [5]int{1, 2, 3, 4, 5}

    resultado := Soma(numeros)
    esperado := 15

    if esperado != resultado {
        t.Errorf("resultado %d, esperado %d, dado %v", resultado, esperado, numeros)
    }
}
```

Arrays têm uma *capacidade fixa* que é definida quando você declara a variável. Podemos inicializar um array de duas formas:

* \[N]tipo{valor1, valor2, ..., valorN}, como `numeros := [5]int{1, 2, 3, 4, 5}`
* \[...]tipo{valor1, valor2, ..., valorN}, como `numbers := [...]int{1, 2, 3, 4, 5}`

Às vezes é útil também mostrarmos as entradas da função na mensagem de erro. Para isso estamos usando o formatador `%v`, que é o formato "padrão" e funciona bem com arrays.

[Leia mais sobre formatação de strings aqui](https://golang.org/pkg/fmt/)

## Execute o teste

Ao executar `go test`, o compilador vai falhar com `./soma_test.go:10:15: undefined: Soma`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhado

Em `soma.go`:

```go
package main

func Soma(numeros [5]int) int {
    return 0
}
```

Agora seu teste deve falhar com uma *mensagem clara de erro*:

`soma_test.go:13: resultado 0, esperado 15, dado [1 2 3 4 5]`

## Escreva código o suficiente para fazer o teste passar

```go
func Soma(numeros [5]int) int {
    soma := 0
    for i := 0; i < 5; i++ {
        soma += numeros[i]
    }
    return soma
}
```

Para receber o valor de um array em uma posição específica, basta usar a sintaxe `array[índice]`. Nesse caso, estamos usando o `for` para percorrer cada posição do array (que tem 5 posições) e somar cada valor na variável `soma`.

## Refatoração

Vamos apresentar o [`range`](https://gobyexample.com/range) para nos ajudar a limpar o código:

```go
func Soma(numeros [5]int) int {
    soma := 0
    for _, numero := range numeros {
        soma += numero
    }
    return soma
}
```

O `range` permite que você percorra um array. Sempre que é chamado, retorna dois valores: o índice e o valor. Decidimos ignorar o valor índice usando `_` [*blank identifier*](https://golang.org/doc/effective_go.html#blank).

### Arrays e seus tipos

Uma propriedade interessante dos arrays é que seu tamanho é relacionado ao seu tipo. Se tentar passar um `[4]int` dentro da função que espera `[5]int`, ela não vai compilar. Elas são de tipos diferentes e é a mesma coisa que tentar passar uma `string` para uma função que espera um `int`.

Você pode estar pensando que é bastante complicado que arrays tenham tamanho fixo, não é? Só que na maioria das vezes, você provavelmente não vai usá-los!

O Go tem *slices*, em que você não define o tamanho da coleção e, graças a isso, pode ter qualquer tamanho.

O próximo requerimento será somar coleções de tamanhos variados.

## Escreva o teste primeiro

Agora vamos usar o [tipo slice](https://golang.org/doc/effective_go.html#slices) que nos permite ter coleções de qualquer tamanho. A sintaxe é bem parecida com a dos arrays e você só precisa omitir o tamanho quando declará-lo.

`meuSlice := []int{1,2,3}` ao invés de `meuArray := [3]int{1,2,3}`

```go
func TestSoma(t *testing.T) {

    t.Run("coleção de 5 números", func(t *testing.T) {
        numeros := [5]int{1, 2, 3, 4, 5}

        resultado := Soma(numeros)
        esperado := 15

        if resultado != esperado {
            t.Errorf("resultado %d, want %d, dado %v", resultado, esperado, numeros)
        }
    })

    t.Run("coleção de qualquer tamanho", func(t *testing.T) {
        numeros := []int{1, 2, 3}

        resultado := Soma(numeros)
        esperado := 6

        if resultado != esperado {
            t.Errorf("resultado %d, esperado %d, dado %v", resultado, esperado, numeros)
        }
    })

}
```

## Execute o teste

Isso não vai compilar.

`./soma_test.go:22:13: cannot use numbers (type []int) as type [5]int in argument to Soma`

`não é possível usar números (tipo []int) como tipo [5]int no argumento para Soma`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhado

Para resolver o problema, podemos:

* Alterar a API existente mudando o argumento de `Soma` para um slice ao invés de um array.Quando fazemos isso, vamos saber que podemos ter arruinado do dia de alguém, porque nosso *outro* teste não vai compilar!
* Criar uma nova função

No nosso caso, mais ninguém está usando nossa função. Logo, ao invés de ter duas funções para manter, vamos usar apenas uma.

```go
func Soma(numeros []int) int {
    soma := 0
    for _, numero := range numeros {
        soma += numero
    }
    return soma
}
```

Se tentar rodar os testes eles ainda não vão compilar. Você vai ter que alterar o primeiro teste e passar um slice ao invés de um array.

## Escreva código o suficiente para fazer o teste passar

Nesse caso, para arrumar os problemas de compilação, tudo o que precisamos fazer aqui é fazer os testes passarem!

## Refatoração

Nós já refatoramos a função `Soma` e tudo o que fizemos foi mudar os arrays para slices. Logo, não há muito o que fazer aqui. Lembre-se que não devemos abandonar nosso código de teste na etapa de refatoração e precisamos fazer alguma coisa aqui.

```go
func TestSoma(t *testing.T) {

    t.Run("coleção de 5 números", func(t *testing.T) {
        numeros := []int{1, 2, 3, 4, 5}

        resultado := Soma(numeros)
        esperado := 15

        if resultado != esperado {
            t.Errorf("resultado %d, esperado %d, dado, %v", resultado, esperado, numeros)
        }
    })

    t.Run("coleção de qualquer tamanho", func(t *testing.T) {
        numeros := []int{1, 2, 3}

        resultado := Soma(numeros)
        esperado := 6

        if resultado != esperado {
            t.Errorf("resultado %d, esperado %d, dado %v", resultado, esperado, numeros)
        }
    })

}
```

É importante questionar o valor dos seus testes. Ter o máximo de testes possível não deve ser o objetivo e sim ter o máximo de *confiança* possível na sua base de código. Ter testes demais pode se tornar um problema real e só adiciona mais peso na manutenção. **Todo teste tem um custo**.

No nosso caso, dá para perceber que ter dois testes para essa função é redundância. Se funciona para um slice de determindo tamanho, é muito provável que funciona para um slice de qualquer tamanho (dentro desse escopo).

A ferramenta de testes nativa do Go tem a funcionalidade de [cobertura de código](https://blog.golang.org/cover) que te ajuda a identificar áreas do seu código que você não cobriu. Já adianto que ter 100% de cobertura não deve ser seu objetivo; é apenas uma ferramenta para te dar uma ideia da sua cobertura. De qualquer forma, se você aplicar o TDD, é bem provável que chegue bem perto dos 100% de cobertura.

Tente executar `go test -cover` no terminal.

Você deve ver:

```bash
PASS
coverage: 100.0% of statements
```

Agora apague um dos testes e verifique a cobertura novamente.

Agora que estamos felizes com nossa função bem testada, você deve salvar seu trabalho incrível com um commit antes de partir para o próximo desafio.

Precisamos de uma nova função chamada `SomaTudo`, que vai receber uma quantidade variável de slices e devolver um novo slice contendo as somas de cada slice recebido.

Por exemplo:

`SomaTudo([]int{1,2}, []int{0,9})` deve retornar `[]int{3, 9}`

ou

`SomaTudo([]int{1,1,1})` deve retornar `[]int{3}`

## Escreva o teste primeiro

```go
func TestSomaTudo(t *testing.T) {

    resultado := SomaTudo([]int{1,2}, []int{0,9})
    esperado := []int{3, 9}

    if resultado != esperado {
        t.Errorf("resultado %v esperado %v", resultado, esperado)
    }
}
```

## Execute o teste

`./soma_test.go:23:9: undefined: SomaTudo`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhado

Precisamos definir o SomaTudo de acordo com o que nosso teste precisa.

O Go te permite escrever [*funções variádicas*](https://gobyexample.com/variadic-functions) em que a quantidade de argumentos podem variar.

```go
func SomaTudo(numerosParaSomar ...[]int) (somas []int) {
    return
}
```

Pode tentar compilar, mas nossos testes não vão funcionar!

`./soma_test.go:26:9: invalid operation: resultado != esperado (slice can only be compared to nil)`

`operação inválida: recebido != esperado (slice só pode ser comparado a nil`

O Go não te deixa usar operadores de igualdade com slices. *É possível* escrever uma função que percorre cada slice `recebido` e `esperado` e verificar seus valores, mas por praticidade podemos usar o [`reflect.DeepEqual`](https://golang.org/pkg/reflect/#DeepEqual) que é útil para verificar se *duas variáveis* são iguais.

```go
func TestSomaTudo(t *testing.T) {

    recebido := SomaTudo([]int{1,2}, []int{0,9})
    esperado := []int{3, 9}

    if !reflect.DeepEqual(recebido, esperado) {
        t.Errorf("recebido %v esperado %v", recebido, esperado)
    }
}
```

(coloque `import reflect` no topo do seu arquivo para ter acesso ao `DeepEqual`)

É importante saber que o `reflect.DeepEqual` não tem "segurança de tipos", ou seja, o código vai compilar mesmo se você tiver feito algo estranho. Para ver isso em ação, altere o teste temporariamente para:

```go
func TestSomaTudo(t *testing.T) {

    recebido := SomaTudo([]int{1,2}, []int{0,9})
    esperado := "joao"

    if !reflect.DeepEqual(recebido, esperado) {
        t.Errorf("recebido %v, esperado %v", recebido, esperado)
    }
}
```

O que fizemos aqui foi comparar um `slice` com uma `string`. Isso não faz sentido, mas o teste compila! Logo, apesar de ser uma forma simples de comparar slices (e outras coisas), você deve tomar cuidado quando for usar o `reflect.DeepEqual`.

Volte o teste da forma como estava e execute-o. Você deve ter a saída do teste com uma mensagem tipo:

`soma_test.go:30: recebido [], esperado [3 9]`

## Escreva código o suficiente para fazer o teste passar

O que precisamos fazer é percorrer as variáveis recebidas como argumento, calcular a soma com nossa função `Soma` de antes e adicioná-la ao slice que vamos retornar:

```go
func SomaTudo(numerosParaSomar ...[]int) (somas []int) {
    quantidadeDeNumeros := len(numerosParaSomar)
    somas = make([]int, quantidadeDeNumeros)

    for i, numeros := range numerosParaSomar {
        somas[i] = Soma(numeros)
    }

    return
 }
```

Muitas coisas novas para aprender!

Há uma nova forma de criar um slice. O `make` te permite criar um slice com uma capacidade inicial de `len` de `numerosParaSomar` que precisamos percorrer.

Você pode indexar slices como arrays com `meuSlice[N]` para obter seu valor ou designá-lo a um novo valor com `=`.

Agora o teste deve passar.

## Refatoração

Como mencionado, slices têm uma capacidade. Se você tiver um slice com uma capacidade de 2 e tentar fazer uma atribuição como `meuSlice[10] = 1`, vai receber um erro em *tempo de execução*.

No entanto, você pode usar a função `append`, que recebe um slice e um novo valor e retorna um novo slice com todos os itens dentro dele.

```go
func SomaTudo(numerosParaSomar ...[]int) []int {
    var somas []int
    for _, numeros := range numerosParaSomar {
        somas = append(somas, Soma(numeros))
    }

    return somas
}
```

Nessa implementação, nos preocupamos menos sobre capacidade. Começamos com um slice vazio `somas` e o anexamos ao resultado de `Soma` enquanto percorremos as variáveis recebidas como argumento.

Nosso próprio requisito é alterar o `SomaTudo` para `SomaTodoOResto`, onde agora calcula os totais de todos os "finais" de cada slice. O final de uma coleção é todos os itens com exceção do primeiro (a "cabeça").

## Escreva o teste primeiro

```go
func TestSomaTodoOResto(t *testing.T) {
    resultado := SomaTodoOResto([]int{1,2}, []int{0,9})
    esperado := []int{2, 9}

    if !reflect.DeepEqual(resultado, esperado) {
        t.Errorf("resultado %v, esperado %v", resultado, esperado)
    }
}
```

## Execute o teste

`./soma_test.go:26:9: undefined: SomaTodoOResto`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhado

Renomeie a função para `SomaTodoOResto` e volte a executar o teste.

`soma_test.go:30: resultado [3 9], esperado [2 9]`

## Escreva código o suficiente para fazer o teste passar

```go
func SomaTodoOResto(numerosParaSomar ...[]int) []int {
    var somas []int
    for _, numeros := range numerosParaSomar {
        final := numeros[1:]
        somas = append(somas, Soma(final))
    }

    return somas
}
```

Slices podem ser "fatiados"! A sintaxe usada é `slice[inicio:final]`. Se você omitir o valor de um dos lados dos `:` ele captura tudo do lado omitido. No nosso caso, quando usamos `numeros[1:]`, estamos dizendo "pegue da posição 1 até o final". É uma boa ideia investir um tempo escrevend outros testes com slices e brincar com o operador slice para criar mais familiaridade com ele.

## Refatoração

Não tem muito o que refatorar dessa vez.

O que acha que aconteceria se você passar um slice vazio para a nossa função? Qual é o "final" de um slice vazio? O que acontece quando você fala para o Go capturar todos os elementos de `meuSliceVazio[1:]`?

## Escreva o teste primeiro

```go
func TestSomaTodoOResto(t *testing.T) {

    t.Run("faz as somas de alguns slices", func(t *testing.T) {
        resultado := SomaTodoOResto([]int{1,2}, []int{0,9})
        esperado := []int{2, 9}

        if !reflect.DeepEqual(resultado, esperado) {
            t.Errorf("resultado %v, esperado %v", resultado, esperado)
        }
    })

    t.Run("soma slices vazios de forma segura", func(t *testing.T) {
        resultado := SomaTodoOResto([]int{}, []int{3, 4, 5})
        esperado := []int{0, 9}

        if !reflect.DeepEqual(resultado, esperado) {
            t.Errorf("resultado %v, esperado %v", resultado, esperado)
        }
    })

}
```

## Execute o teste

```bash
panic: runtime error: slice bounds out of range [recovered]
    panic: runtime error: slice bounds out of range
```

`pânico: erro em tempo de execução: fora da capacidade do slice`

Oh, não! É importante perceber que o test *foi compilado*, esse é um erro em tempo de execução. Erros em tempo de compilação são nossos amigos, porque nos ajudam a escrever softwares que funcionam. Erros em tempo de execução são nosso inimigos, porque afetam nossos usuários.

## Escreva código o suficiente para fazer o teste passar

```go
func SomaTodoOResto(numerosParaSomar ...[]int) []int {
    var somas []int
    for _, numeros := range numerosParaSomar {
        if len(numeros) == 0 {
            somas = append(somas, 0)
        } else {
            final := numeros[1:]
            somas = append(somas, Soma(final))
        }
    }

    return somas
}
```

## Refatoração

Nossos testes têm código repetido em relação à asserção de novo. Vamos encapsular isso em uma função:

```go
func TestSomaTodoOResto(t *testing.T) {

    verificaSomas := func(t *testing.T, resultado, esperado []int) {
        t.Helper()
        if !reflect.DeepEqual(resultado, esperado) {
            t.Errorf("resultado %v, esperado %v", resultado, esperado)
        }
    }

    t.Run("faz a soma do resto", func(t *testing.T) {
        resultado := SomaTodoOResto([]int{1, 2}, []int{0, 9})
        esperado := []int{2, 9}
        verificaSomas(t, resultado, esperado)
    })

    t.Run("soma slices vazios de forma segura", func(t *testing.T) {
        resultado := SomaTodoOResto([]int{}, []int{3, 4, 5})
        esperado := []int{0, 9}
        verificaSomas(t, resultado, esperado)
    })

}
```

Um efeito colateral útil disso é que adiciona um pouco de segurança de tipos no nosso código. Se uma pessoa espertinha adicionar um novo teste com `verificaSomas(t, resultado, "luisa")` o compilador vai pará-lo antes que algo errado aconteça.

```bash
$ go test
./soma_test.go:52:21: cannot use "luisa" (type string) as type []int in argument to verificaSomas
```

`não é possível usar "luisa" (tipo string) como tipo []int no argumento para verificaSomas`

## Resumindo

Falamos sobre:

* Arrays
* Slices
* Várias formas de criá-las
* Como eles têm uma capacidade *fixa*, mas é posível criar novos slices de antigos usando `append`
* Como "fatiar" slices!
* `len` obtém o tamanho de um array ou slice
* Ferramenta de cobertura de testes
* `reflect.DeepEqual` e por que é útil, mas pode diminuir a segurança de tipos do seu código

Usamos slices e arrays com inteiros, mas eles também funcionam com qualquer outro tipo, incluindo até os próprios arrays/slices. Logo, você pode declarar uma variável de `[][]string` se precisar.

[Dê uma olhada no post sobre slices no blog de Go](https://blog.golang.org/go-slices-usage-and-internals) para saber mais sobre slices. Tente escrever mais testes para demonstrar o que você aprendeu com a leitura.

Outra forma útil de brincar com Go ao invés de escrever testes é o Go playground. Você pode testar mais coisas lá e você pode compartilhar seu código facilmente se precisar tirar dúvidas. [Criei um exemplo com um slice para testar lá.](https://play.golang.org/p/ICCWcRGIO68)


# Estruturas, métodos e interfaces

[**Você pode encontrar todos os códigos desse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/estruturas-metodos-e-interfaces)

Suponha que precisamos de algum código de geometria para calcular o perímetro de um retângulo dado uma altura e largura. Podemos escrever uma função `Perimetro(largura float64, altura float64)`, onde `float64` representa números em ponto flutuante como `123.45`.

O ciclo de TDD deve ser mais familiar para você agora.

## Escreva o teste primeiro

```go
func TestPerimetro(t *testing.T) {
	resultado := Perimetro(10.0, 10.0)
	esperado := 40.0

	if resultado != esperado {
		t.Errorf("resultado %.2f esperado %.2f", resultado, esperado)
	}
}
```

Viu a nova string de formatação? O `f` é para nosso `float64` e o `.2` significa imprimir duas casas decimais.

## Execute o teste

`./formas_test.go:6:9: undefined: Perimetro`

`indefinido: Perimetro`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhando

```go
func Perimetro(largura float64, altura float64) float64 {
    return 0
}
```

Resulta em `formas_test.go:10: resultado 0, esperado 40`.

## Escreva código o suficiente para fazer o teste passar

```go
func Perimetro(largura float64, altura float64) float64 {
	return 2 * (largura + altura)
}
```

Por enquanto, tudo fácil. Agora vamos criar uma função chamada `Area(largura, altura float64)` que retorna a área de um retângulo.

Tente fazer isso sozinho, segundo o ciclo de TDD.

Você deve terminar com os testes como estes:

```go
func TestPerimetro(t *testing.T) {
	resultado := Perimetro(10.0, 10.0)
	esperado := 40.0

	if resultado != esperado {
		t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
	}
}

func TestArea(t *testing.T) {
	resultado := Area(12.0, 6.0)
	esperado := 72.0

	if resultado != esperado {
		t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
	}
}
```

E código como este:

```go
func Perimetro(largura float64, altura float64) float64 {
	return 2 * (largura + altura)
}

func Area(largura float64, altura float64) float64 {
	return largura * altura
}
```

## Refatoração

Nosso código faz o trabalho, mas não contém nada explícito sobre retângulos. Uma pessoa descuidada poderia tentar passar a largura e altura de um triângulo para esta função sem perceber que ela retornará uma resposta errada.

Podemos apenas dar para a função um nome mais específico como `AreaDoRetangulo`. Uma solução mais limpa é definir nosso próprio *tipo* chamado `Retangulo` que encapsula este conceito para nós.

Podemos criar um tipo simples usando uma **struct** (estrutura). [Uma struct](https://golang.org/ref/spec#Struct_types) é apenas uma coleção nomeada de campos onde você pode armazenar dados.

Declare uma `struct` assim:

```go
type Retangulo struct {
	Largura float64
	Altura  float64
}
```

Agora vamos refatorar os testes para usar `Retangulo` em vez de um simples `float64`.

```go
func TestPerimetro(t *testing.T) {
	retangulo := Retangulo{10.0, 10.0}
	resultado := Perimetro(retangulo)
	esperado := 40.0

	if resultado != esperado {
		t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
	}
}

func TestArea(t *testing.T) {
	retangulo := Retangulo{12.0, 6.0}
	resultado := Area(retangulo)
	esperado := 72.0

	if resultado != esperado {
		t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
	}
}
```

Lembre de rodar seus testes antes de tentar corrigir. Você deve ter erro útil como:

```
./formas_test.go:7:18: not enough arguments in call to Perimetro
    have (Retangulo)
    esperado (float64, float64)
```

Você pode acessar os campos de uma `struct` com a sintaxe `minhaStruct.campo`.

Mude as duas funções para corrigir o teste.

```go
func Perimetro(retangulo Retangulo) float64 {
	return 2 * (retangulo.Largura + retangulo.Altura)
}

func Area(retangulo Retangulo) float64 {
	return retangulo.Largura * retangulo.Altura
}
```

Espero que você concorde que passar um `Retangulo` para a função mostra nossa intenção com mais clareza, mas existem mais benefícios em usar `structs` que já vamos entender.

Nosso próximo requisito é escrever uma função `Area` para círculos.

## Escreva o teste primeiro

```go
func TestArea(t *testing.T) {
	t.Run("retângulos", func(t *testing.T) {
		retangulo := Retangulo{12.0, 6.0}
		resultado := Area(retangulo)
		esperado := 72.0

		if resultado != esperado {
			t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
		}
	})

	t.Run("círculos", func(t *testing.T) {
		circulo := Circulo{10}
		resultado := Area(circulo)
		esperado := 314.1592653589793

		if resultado != esperado {
			t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
		}
	})
}
```

## Execute o teste

`./formas_test.go:28:13: undefined: Circulo`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhando

Precisamos definir nosso tipo `Circulo`.

```go
type Circulo struct {
	Raio float64
}
```

Agora rode os testes novamente.

`./formas_test.go:29:14: cannot use circulo (type Circulo) as type Retangulo in argument to Area`

Algumas linguagens de programação permitem você fazer algo como:

```go
func Area(circulo Circulo) float64 { ... }
func Area(retangulo Retangulo) float64 { ... }
```

Mas em Go você não pode:

`./formas.go:20:32: Area redeclared in this block`

Temos duas escolhas:

* Podemos ter funções com o mesmo nome declaradas em *pacotes* diferentes. Então, poderíamos criar nossa `Area(Circulo)` em um novo *pacote*, só que isso parece um exagero aqui.
* Em vez disso, podemos definir [*métodos*](https://golang.org/ref/spec#Method_declarations) em nosso mais novo tipo definido.

### O que são métodos?

Até agora só escrevemos *funções*, mas temos usado alguns métodos. Quando chamamos `t.Errorf`, nós chamamos o método `Errorf` na instância de nosso `t` (`testing.T`).

Um método é uma função com um receptor. Uma declaração de método vincula um identificador e o nome do método a um método e associa o método com o tipo base do receptor.

Métodos são muito parecidos com funções, mas são chamados invocando-os em uma instância de um tipo específico.

Enquanto você chama funções onde quiser, como por exemplo em `Area(retangulo)`, você só pode chamar métodos em "coisas" específicas.

Um exemplo ajudará. Então, vamos mudar nossos testes primeiro para chamar métodos em vez de funções, e, em seguida, corrigir o código.

```go
func TestArea(t *testing.T) {
	t.Run("retângulos", func(t *testing.T) {
		retangulo := Retangulo{12.0, 6.0}
		resultado := retangulo.Area()
		esperado := 72.0

		if resultado != esperado {
			t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
		}
	})

	t.Run("círculos", func(t *testing.T) {
		circulo := Circulo{10}
		resultado := circulo.Area()
		esperado := 314.1592653589793

		if resultado != esperado {
			t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
		}
	})
}
```

Se rodarmos os testes agora, recebemos:

```
./formas_test.go:19:19: retangulo.Area undefined (type Retangulo has no field or method Area)
./formas_test.go:29:16: circulo.Area undefined (type Circulo has no field or method Area)
```

> type Circulo has no field or method Area

Gostaria de reforçar o quão grandioso o compilador é. É muito importante ter tempo para ler lentamente as mensagens de erro que você recebe, pois isso te ajudará a longo prazo.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhando

Vamos adicionar alguns métodos para nossos tipos:

```go
type Retangulo struct {
    Largura  float64
    Altura float64
}

func (r Retangulo) Area() float64  {
    return 0
}

type Circulo struct {
    Raio float64
}

func (c Circulo) Area() float64  {
    return 0
}
```

A sintaxe para declaração de métodos é quase a mesma que usamos para funções e isso acontece porque eles são muito parecidos. A única diferença é a sintaxe para o método receptor: `func (nomeDoReceptor TipoDoReceptor) NomeDoMetodo(argumentos)`.

Quando seu método é chamado em uma variável desse tipo, você tem sua referência para o dado através da variável `nomeDoReceptor`. Em muitas outras linguagens de programação isto é feito implicitamente e você acessa o receptor através de `this`.

É uma convenção em Go que a variável receptora seja a primeira letra do tipo em minúsculo.

```go
r Retangulo
```

Se você executar novamente os testes, eles devem compilar e dar alguma saída do teste falhando.

## Escreva código suficiente para fazer o teste passar

Agora vamos fazer nossos testes de retângulo passarem corrigindo nosso novo método.

```go
func (r Retangulo) Area() float64  {
    return r.Largura * r.Altura
}
```

Se você executar novamente os testes, aqueles de retângulo devem passar, mas os de círculo ainda falham.

Para fazer a função `Area` de círculo passar, vamos emprestar a constante `Pi` do pacote `math` (lembre-se de importá-lo).

```go
func (c Circulo) Area() float64  {
    return math.Pi * c.Raio * c.Raio
}
```

## Refatoração

Existe duplicação em nossos testes.

Tudo o que queremos fazer é pegar uma coleção de *formas*, chamar o método `Area()` e então verificar o resultado.

Queremos ser capazes de escrever um tipo de função `verificaArea` que permita passar tanto `Retangulo` quanto `Circulo`, mas falhe ao compilar se tentarmos passar algo que não seja uma *forma*.

Com Go, podemos trabalhar dessa forma com **interfaces**.

[Interfaces](https://golang.org/ref/spec#Interface_types) são um conceito muito poderoso em linguagens de programação estaticamente tipadas, como Go, porque permitem que você crie funções que podem ser usadas com diferentes tipos e permite a criação de código altamente desacoplado, mantendo ainda a segurança de tipos.

Vamos apresentar isso refatorando nossos testes.

```go
func TestArea(t *testing.T) {
	verificaArea := func(t *testing.T, forma Forma, esperado float64) {
		t.Helper()
		resultado := forma.Area()

		if resultado != esperado {
			t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
		}
	}

	t.Run("retângulos", func(t *testing.T) {
		retangulo := Retangulo{12.0, 6.0}
		verificaArea(t, retangulo, 72.0)
	})

	t.Run("círculos", func(t *testing.T) {
		circulo := Circulo{10}
		verificaArea(t, circulo, 314.1592653589793)
	})
}
```

Estamos criando uma função auxiliar como fizemos em outros exercícios, mas desta vez estamos pedindo que uma `Forma` seja passada. Se tentarmos chamá-la com algo que não seja uma *forma*, não vai compilar.

Como algo se torna uma *forma*? Precisamos apenas falar para o Go o que é uma `Forma` usando uma declaração de interface.

```go
type Forma interface {
	Area() float64
}
```

Estamos criando um novo `tipo`, assim como fizemos com `Retangulo` e `Circulo`, mas desta vez é uma `interface` em vez de uma `struct`.

Uma vez adicionado isso ao código, os testes passarão.

### Peraí, como assim?

A interface em Go bem diferente das interfaces na maioria das outras linguagens de programação. Normalmente você tem que escrever um código para dizer que `meu tipo Foo implementa a interface Bar`.

Só que no nosso caso:

* `Retangulo` tem um método chamado `Area` que retorna um `float64`, então satisfaz a interface `Forma`.
* `Circulo` tem um método chamado `Area` que retorna um `float64`, então satisfaz a interface `Forma`.
* `string` não tem esse método, então não satisfaz a interface.
* etc.

Em Go a **resolução de interface é implícita**. Se o tipo que você passar combinar com o que a interface está esperando, o código será compilado.

### Desacoplando

Veja como nossa função auxiliar não precisa se preocupar se a *forma* é um `Retangulo` ou um `Circulo` ou um `Triangulo`. Ao declarar uma interface, a função auxiliar está *desacoplada* de tipos concretos e tem apenas o método que precisa para fazer o trabalho.

Este tipo de abordagem - de usar interfaces para declarar **somente o que você precisa** - é muito importante no desenvolvimento de software e será coberto mais detalhadamente nas próximas seções.

## Refatoração adicional

Agora que você conhece as `structs`, podemos apresentar os "table driven tests" (testes orientados por tabela).

[Table driven tests](https://github.com/golang/go/wiki/TableDrivenTests) são úteis quando você quer construir uma lista de casos de testes que podem ser testados da mesma forma.

```go
func TestArea(t *testing.T) {
	testesArea := []struct {
		forma    Forma
		esperado float64
	}{
		{Retangulo{12, 6}, 72.0},
		{Circulo{10}, 314.1592653589793},
	}

	for _, tt := range testesArea {
		resultado := tt.forma.Area()
		if resultado != tt.esperado {
			t.Errorf("resultado %.2f, esperado %.2f", resultado, tt.esperado)
		}
	}
}
```

A única sintaxe nova aqui é a criação de uma "struct anônima", `testesArea`. Estamos declarando um slice de structs usando `[]struct` com dois campos, o `forma` e o `esperado`. Então preenchemos o slice com os casos.

Depois iteramos sobre eles assim como fazemos com qualquer outro slice, usando os campos da struct para executar nossos testes.

Dá para perceber como será muito fácil para uma pessoa inserir uma nova forma, implementar `Area` e então adicioná-la nos casos de teste. Além disso, se for encontrada uma falha em `Area`, é muito fácil adicionar um novo caso de teste para verificar antes de corrigi-la.

*Testes baseados em tabela* podem ser um item valioso em sua caixa de ferramentas, mas tenha certeza de que você precisa da sintaxe extra nos testes. Se você deseja testar várias implementações de uma interface ou se o dado passado para uma função tem muitos requisitos diferentes que precisam de testes, eles podem servir bem.

Vamos demonstrar tudo isso adicionando e testando outra forma; um triângulo.

## Escreva o teste primeiro

Adicionar um teste para nossa nova forma é muito fácil. Simplesmente adicione `{Triangulo{12, 6}, 36.0},` à nossa lista.

```go
func TestArea(t *testing.T) {
	testesArea := []struct {
		forma    Forma
		esperado float64
	}{
		{Retangulo{12, 6}, 72.0},
		{Circulo{10}, 314.1592653589793},
		{Triangulo{12, 6}, 36.0},
	}

	for _, tt := range testesArea {
		resultado := tt.forma.Area()
		if resultado != tt.esperado {
			t.Errorf("resultado %.2f, esperado %.2f", resultado, tt.esperado)
		}
	}
}
```

## Execute o teste

Lembre-se, continue tentando executar o teste e deixe o compilador guiá-lo em direção a solução.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhando

`./formas_test.go:25:4: undefined: Triangulo`

Ainda não definimos `Triangulo`:

```go
type Triangulo struct {
    Base   float64
    Altura float64
}
```

Tente novamente:

```
./formas_test.go:25:8: cannot use Triangulo literal (type Triangulo) as type Forma in field value:
    Triangulo does not implement Forma (missing Area method)
```

`Triangulo não implementa Forma (método Area faltando)`

Isso nos diz que não podemos usar um `Triangulo` como uma `Forma` porque ele não tem um método `Area()`, então adicione uma implementação vazia para fazermos o teste funcionar:

```go
func (t Triangulo) Area() float64 {
    return 0
}
```

Finalmente o código compilou e temos o nosso erro:

`formas_test.go:31: resultado 0.00, esperado 36.00`

## Escreva código suficiente para fazer o teste passar

```go
func (t Triangulo) Area() float64 {
    return (t.Base * t.Altura) * 0.5
}
```

E nossos testes passaram!

## Refatoração

Novamente, a implementação está boa, mas nossos testes podem ser melhorados.

Quando você lê isso:

```go
{Retangulo{12, 6}, 72.0},
{Circulo{10}, 314.1592653589793},
{Triangulo{12, 6}, 36.0},
```

Não está tão claro o que todos os números representam e você deve ter o objetivo de escrever testes que sejam fáceis de entender.

Até agora você viu uma sintaxe para criar instâncias de structs como `MinhaStruct{valor1, valor2}`, mas você pode opcionalmente nomear esses campos.

Vamos ver como isso funciona:

```go
        {forma: Retangulo{largura: 12, altura: 6}, esperado: 72.0},
        {forma: Circulo{Raio: 10}, esperado: 314.1592653589793},
        {forma: Triangulo{Base: 12, altura: 6}, esperado: 36.0},
```

Em [Test-Driven Development by Example](https://g.co/kgs/yCzDLF) Kent Beck refatora alguns testes para um ponto e afirma:

> O teste é lido de forma mais clara, como se fosse uma afirmação da verdade, **não uma sequência de operações**

(ênfase minha)

Agora nossos testes (pelo menos a lista de casos) fazem afirmações da verdade sobre formas e suas áreas.

## Garanta que a saída do seu teste seja útil

Lembra anteriormente quando implementamos `Triangulo` e tivemos um teste falhando? Ele imprimiu `formas_test.go:31: resultado 0.00 esperado, 36.00`.

Nós sabíamos que estava relacionado ao `Triangulo` porque estávamos trabalhando nisso, mas e se uma falha escorregasse para o sistema em um dos 20 casos na tabela? Como alguém saberia qual caso falhou? Não parece ser uma boa experiência. Ela teria que olhar caso a caso para encontrar qual deles está falhando de fato.

Podemos mudar nossa mensagem de erro para `%#v resultado %.2f, esperado %.2f`. A string de formatação `%#v` irá imprimir nossa struct com os valores em seu campo para que as pessoas possam ver imediatamente as propriedades que estão sendo testadas.

Para melhorar a legibilidade de nossos futuros casos de teste, podemos renomear o campo `esperado` para algo mais descritivo como `temArea`.

Uma dica final com testes guiados por tabela é usar `t.Run` e renomear os casos de teste.

Envolvendo cada caso em um `t.Run` você terá uma saída de testes mais limpa em caso de falhas, além de imprimir o nome do caso.

```
--- FAIL: TestArea (0.00s)
    --- FAIL: TestArea/Retangulo (0.00s)
        formas_test.go:33: main.Retangulo{Largura:12, Altura:6} resultado 72.00, esperado 72.10
```

E você pode rodar testes específicos dentro de sua tabela com `go test -run TestArea/Retangulo`.

Aqui está o código final do nosso teste que captura isso:

```go
func TestArea(t *testing.T) {
	testesArea := []struct {
		nome    string
		forma   Forma
		temArea float64
	}{
		{nome: "Retângulo", forma: Retangulo{Largura: 12, Altura: 6}, temArea: 72.0},
		{nome: "Círculo", forma: Circulo{Raio: 10}, temArea: 314.1592653589793},
		{nome: "Triângulo", forma: Triangulo{Base: 12, Altura: 6}, temArea: 36.0},
	}

	for _, tt := range testesArea {
		t.Run(tt.nome, func(t *testing.T) {
			resultado := tt.forma.Area()
			if resultado != tt.temArea {
				t.Errorf("%#v resultado %.2f, esperado %.2f", tt.forma, resultado, tt.temArea)
			}
		})
	}
}
```

## Resumo

Esta foi mais uma prática de TDD, iterando em nossas soluções para problemas matemáticos básicos e aprendendo novos recursos da linguagem motivados por nossos testes.

* Declarar structs para criar seus próprios tipos de dados permite agrupar dados relacionados e torna a intenção do seu código mais clara.
* Declarar interfaces permite que você possa definir funções que podem ser usadas por diferentes tipos ([polimorfismo paramétrico](https://pt.wikipedia.org/wiki/Polimorfismo_paramétrico)).
* Adicionar métodos permite que você possa adicionar funcionalidades aos seus tipos de dados e implementar interfaces.
* Testes baseados em tabela permite que você torne suas asserções mais claras e seus testes mais fáceis de estender e manter.

Este foi um capítulo importante porque agora começamos a definir nossos próprios tipos. Em linguagens estaticamente tipadas como Go, conseguir projetar seus próprios tipos é essencial para construir software que seja fácil de entender, compilar e testar.

Interfaces são uma ótima ferramenta para ocultar a complexidade de outras partes do sistema. Em nosso caso, o *código* de teste auxiliar não precisou conhecer a forma exata que estava afirmando, apenas como "pedir" pela sua área.

Conforme você se familiariza com Go, começa a ver a força real das interfaces e da biblioteca padrão.

Você aprenderá sobre as interfaces definidas na biblioteca padrão que são usadas *em todo lugar* e, implementando-as em relação aos seus próprios tipos, você pode reutilizar rapidamente muitas das ótimas funcionalidades.


# Ponteiros e erros

[**Você pode encontrar todos os códigos deste capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/ponteiros-e-erros)

Aprendemos sobre estruturas na última seção, o que nos possibilita capturar valores com conceito relacionado.

Em algum momento talvez você deseje utilizar estruturas para gerenciar valores, expondo métodos que permita aos usuários mudá-los de um jeito que você possa controlar.

[**Fintechs**](https://www.infowester.com/fintech.php) **amam Go** e uhh bitcoins? Então vamos mostrar um sistema bancário incrível que podemos construir.

Vamos construir uma estrutura de `Carteira` que possamos depositar `Bitcoin`.

## Escreva o teste primeiro

```go
func TestCarteira(t *testing.T) {
    carteira := Carteira{}

    carteira.Depositar(10)

    resultado := carteira.Saldo()
    esperado := 10

    if resultado != esperado {
		t.Errorf("resultado %d, esperado %d", resultado, esperado)
	}
}
```

No [exemplo anterior](/aprenda-go-com-testes/main/primeiros-passos-com-go/estruturas-metodos-e-interfaces) acessamos campos diretamente pelo nome. Entretanto, na nossa *carteira super protegida*, não queremos expor o valor interno para o resto do mundo. Queremos controlar o acesso por meio de métodos.

## Execute o teste

`./carteira_test.go:7:12: undefined: Carteira`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

O compilador não sabe o que uma `Carteira` é, então vamos declará-la.

```go
type Carteira struct { }
```

Agora que declaramos nossa carteira, tente rodar o teste novamente:

```go
./carteira_test.go:9:8: carteira.Depositar undefined (type Carteira has no field or method Depositar)
./carteira_test.go:11:15: carteira.Saldo undefined (type Carteira has no field or method Saldo)
```

Precisamos definir estes métodos.

Lembre-se de apenas fazer o necessário para fazer os testes rodarem. Precisamos ter certeza que nossos testes falhem corretamente com uma mensagem de erro clara.

```go
func (c Carteira) Depositar(quantidade int) {

}

func (c Carteira) Saldo() int {
    return 0
}
```

Se essa sintaxe não for familiar, dê uma lida na seção de estruturas.

Os testes agora devem compilar e rodar:

`carteira_test.go:15: resultado 0, esperado 10`

## Escreva código o suficiente para fazer o teste passar

Precisaremos de algum tipo de variável de *saldo* em nossa estrutura para guardar o valor:

```go
type Carteira struct {
    saldo int
}
```

Em Go, se uma variável, tipo, função e etc, começam com uma letra minúsculo, então esta será privada para *outros pacotes que não seja o que a definiu*.

No nosso caso, queremos que apenas nossos métodos sejam capazes de manipular os valores.

Lembre-se que podemos acessar o valor interno do campo `saldo` usando a variável "receptora".

```go
func (c Carteira) Depositar(quantidade int) {
    c.saldo += quantidade
}

func (c Carteira) Saldo() int {
    return c.saldo
}
```

Com a nossa carreira em Fintechs segura, rode os testes para nos aquecermos para passarmos no teste.

`carteira_test.go:15: resultado 0, esperado 10`

### ????

Ok, isso é confuso. Parece que nosso código deveria funcionar, pois adicionamos nosso novo valor ao saldo e o método Saldo deveria retornar o valor atual.

Em Go, **quando uma função ou um método é invocado, os argumentos são** ***copiados***.

Quando `func (c Carteira) Depositar(quantidade int)` é chamado, o `c` é uma cópia do valor de qualquer lugar que o método tenha sido chamado.

Sem focar em Ciência da Computação, quando criamos um valor (como uma carteira), esse valor é alocado em algum lugar da memória. Você pode descobrir o *endereço* desse bit de memória usando `&meuValor`.

Experimente isso adicionando alguns prints no código:

```go
func TestCarteira(t *testing.T) {
    carteira := Carteira{}

    carteira.Depositar(10)

    resultado := carteira.Saldo()

    fmt.Printf("O endereço do saldo no teste é %v \n", &carteira.saldo)

    esperado := 10

    if resultado != esperado {
        t.Errorf("resultado %d, esperado %d", resultado, esperado)
    }
}
```

```go
func (c Carteira) Depositar(quantidade int) {
    fmt.Printf("O endereço do saldo no Depositar é %v \n", &c.saldo)
    c.saldo += quantidade
}
```

O  é um caractere de escape queeadiciona uma nova linha após imprimir o endereço de memória. Conseguimos acessar o ponteiro para algo com o símbolo de endereço `&`.

Agora rode o teste novamente:

```
O endereço do saldo no Depositar é 0xc420012268
O endereço do saldo no teste é is 0xc420012260
```

Podemos ver que os endereços dos dois saldos são diferentes. Então, quando mudamos o valor de um dos saldos dentro do código, estamos trabalhando em uma cópia do que veio do teste. Portanto, o saldo no teste não é alterado.

Podemos consertar isso com *ponteiros*. [Ponteiros](https://gobyexample.com/pointers) nos permitem *apontar* para alguns valores e então mudá-los. Então, em vez de termos uma cópia da Carteira, usamos um ponteiro para a carteira para que possamos alterá-la.

```go
func (c *Carteira) Depositar(quantidade int) {
    c.saldo += quantidade
}

func (c *Carteira) Saldo() int {
    return c.saldo
}
```

A diferença é que o tipo do argumento é `*Carteira` em vez de `Carteira` que você pode ler como "um ponteiro para uma carteira".

Rode novamente os testes e eles devem passar.

## Refatoração

Dissemos que estávamos fazendo uma carteira Bitcoin, mas até agora não os mencionamos. Estamos usando `int` porque é um bom tipo para contar coisas!

Parece um pouco exagerado criar uma `struct` para isso. `int` é o suficiente nesse contexto, mas não é descritivo o suficiente.

Go permite criarmos novos tipos a partir de tipos existentes.

A sintaxe é `type MeuNome TipoOriginal`

```go
type Bitcoin int

type Carteira struct {
    saldo Bitcoin
}

func (c *Carteira) Depositar(quantidade Bitcoin) {
    c.saldo += quantidade
}

func (c *Carteira) Saldo() Bitcoin {
    return c.saldo
}
```

```go
func TestCarteira(t *testing.T) {

    carteira := Carteira{}

    carteira.Depositar(Bitcoin(10))

    resultado := carteira.Saldo()

    esperado := Bitcoin(10)

    if resultado != esperado {
			t.Errorf("resultado %d, esperado %d", resultado, esperado)
		}
}
```

Para criarmos `Bitcoin`, basta usar a sintaxe `Bitcoin(999)`.

Ao fazermos isso, estamos criando um novo tipo e podemos declarar *métodos* nele. Isto pode ser muito útil quando queremos adicionar funcionalidades de domínios específicos a tipos já existentes.

Vamos implementar um [Stringer](https://golang.org/pkg/fmt/#Stringer) para o Bitcoin:

```go
type Stringer interface {
        String() string
}
```

Essa interface é definida no pacote `fmt` e permite definir como seu tipo é impresso quando utilizado com o operador de string `%s` em prints.

```go
func (b Bitcoin) String() string {
    return fmt.Sprintf("%d BTC", b)
}
```

Como podemos ver, a sintaxe para criar um método em um tipo definido por nós é a mesma que a utilizada em uma struct.

Agora precisamos atualizar nossas impressões de strings no teste para que usem `String()`.

```go
    if resultado != esperado {
		t.Errorf("resultado %s, esperado %s", resultado, esperado)
	}
```

Para ver funcionando, quebre o teste de propósito para que possamos ver:

`carteira_test.go:18: resultado 10 BTC, esperado 20 BTC`

Isto deixa mais claro o que está acontecendo em nossos testes.

O próximo requisito é criar uma função de `Retirar`.

## Escreva o teste primeiro

É basicamente o aposto da função `Depositar()`:

```go
func TestCarteira(t *testing.T) {
    t.Run("Depositar", func(t *testing.T) {
        carteira := Carteira{}

        carteira.Depositar(Bitcoin(10))

        resultado := carteira.Saldo()

        esperado := Bitcoin(10)

        if resultado != esperado {
			t.Errorf("resultado %s, esperado %s", resultado, esperado)
		}
    })

    t.Run("Retirar", func(t *testing.T) {
        carteira := Carteira{saldo: Bitcoin(20)}

        carteira.Retirar(Bitcoin(10))

        resultado := carteira.Saldo()

        esperado := Bitcoin(10)

        if resultado != esperado {
			t.Errorf("resultado %s, esperado %s", resultado, esperado)
		}
    })
}
```

## Execute o teste

`./carteira_test.go:26:9: carteira.Retirar undefined (type Carteira has no field or method Retirar)`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (c *Carteira) Retirar(quantidade Bitcoin) {
}
```

`carteira_test.go:33: resultado 20 BTC, esperado 10 BTC`

## Escreva código o suficiente para fazer o teste passar

```go
func (c *Carteira) Retirar(quantidade Bitcoin) {
    c.saldo -= quantidade
}
```

## Refatoração

Há algumas duplicações em nossos testes, vamos refatorar isso.

```go
func TestCarteira(t *testing.T) {
    confirmaSaldo := func(t *testing.T, carteira Carteira, esperado Bitcoin) {
        t.Helper()
		resultado := carteira.Saldo()

		if resultado != esperado {
			t.Errorf("resultado %s, esperado %s", resultado, esperado)
		}
    }

    t.Run("Depositar", func(t *testing.T) {
		carteira := Carteira{}
		carteira.Depositar(Bitcoin(10))
		confirmaSaldo(t, carteira, Bitcoin(10))
	})

	t.Run("Retirar", func(t *testing.T) {
		carteira := Carteira{saldo: Bitcoin(20)}
		carteira.Retirar(10)
		confirmaSaldo(t, carteira, Bitcoin(10))
	})
}
```

O que aconteceria se você tentasse `Retirar` mais do que há de saldo na conta? Por enquanto, nossos requisitos são assumir que não há nenhum tipo de cheque-especial.

Como sinalizamos um problema quando estivermos usando `Retirar` ?

Em Go, se você quiser indicar um erro, sua função deve retornar um `err` para que quem a chamou possar verificá-lo e tratá-lo.

Vamos tentar fazer isso em um teste.

## Escreva o teste primeiro

```go
t.Run("Retirar com saldo insuficiente", func(t *testing.T) {
    saldoInicial := Bitcoin(20)
	carteira := Carteira{saldoInicial}
	erro := carteira.Retirar(Bitcoin(100))

	confirmaSaldo(t, carteira, saldoInicial)

    if erro == nil {
        t.Error("Esperava um erro mas nenhum ocorreu")
    }
})
```

Queremos que `Retirar` retorne um erro se tentarmos retirar mais do que temos e o saldo deverá continuar o mesmo.

Verificamos se um erro foi retornado falhando o teste se o valor for `nil`.

`nil` é a mesma coisa que `null` de outras linguagens de programação.

Erros podem ser `nil`, porque o tipo do retorno de `Retirar` vai ser `error`, que é uma interface. Se você vir uma função que tem argumentos ou retornos que são interfaces, eles podem ser nulos.

Do mesmo jeito que `null`, se tentarmos acessar um valor que é `nil`, isso irá disparar um **pânico em tempo de execução**. Isso é ruim! Devemos ter certeza que tratamos os valores nulos.

## Execute o teste

`./carteira_test.go:31:25: carteira.Retirar(Bitcoin(100)) used as value`

Talvez não esteja tão claro, mas nossa intenção era apenas invocar a função `Retirar` e ela nunca irá retornar um valor pois o saldo será diretamente subtraído com o ponteiro e a função deve apenas retornar o erro (se houver). Para fazer compilar, precisaremos mudar a função para que retorne um tipo.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (c *Carteira) Retirar(quantidade Bitcoin) error {
    c.saldo -= quantidade
    return nil
}
```

Novamente, é muito importante escrever apenas o suficiente para compilar. Corrigimos o método `Retirar` para retornar `error` e por enquanto temos que retornar *alguma coisa*, então vamos apenas retornar `nil` .

## Escreva código o suficiente para fazer o teste passar

```go
func (c *Carteira) Retirar(quantidade Bitcoin) error {
    if quantidade > c.saldo {
        return errors.New("eita")
    }

    c.saldo -= quantidade
    return nil
}
```

Lembre-se de importar `errors`.

`errors.New` cria um novo `error` com a mensagem escolhida.

## Refatoração

Vamos fazer um método auxiliar de teste para nossa verificação de erro para deixar nosso teste mais legível.

```go
confirmaErro := func(t *testing.T, erro error) {
	t.Helper()
	if erro == nil {
		t.Error("esperava um erro, mas nenhum ocorreu.")
	}
}
```

E em nosso teste:

```go
t.Run("Retirar com saldo insuficiente", func(t *testing.T) {
    saldoInicial := Bitcoin(20)
	carteira := Carteira{saldoInicial}
	erro := carteira.Retirar(Bitcoin(100))

	confirmaSaldo(t, carteira, saldoInicial)
	confirmaErro(t, erro)
})
```

Espero que, ao retornamos um erro do tipo "eita", você pense que *devêssemos* deixar mais claro o que ocorreu, já que esta não parece uma informação útil para nós.

Assumindo que o erro enfim foi retornado para o usuário, vamos atualizar nosso teste para verificar o tipo espcífico de mensagem de erro ao invés de apenas verificar se um erro existe.

## Escreva o teste primeiro

Atualize nosso helper para comparar com uma `string`:

```go
confirmaErro := func(t *testing.T, valor error, esperado string) {
    t.Helper()
	if valor == nil {
		t.Fatal("esperava um erro, mas nenhum ocorreu")
	}
    
 	resultado := valor.Error()

	if resultado != esperado {
		t.Errorf("resultado %s, esperado %s", resultado, esperado)
	}
}
```

E então atualize o invocador:

```go
t.Run("Retirar saldo insuficiente", func(t *testing.T) {
    saldoInicial := Bitcoin(20)
	carteira := Carteira{saldoInicial}
	erro := carteira.Retirar(Bitcoin(100))

	confirmaSaldo(t, carteira, saldoInicial)
    confirmaErro(t, erro, "não é possível retirar: saldo insuficiente")
})
```

Usamos o `t.Fatal` que interromperá o teste se for chamado. Isso é feito porque não queremos fazer mais asserções no erro retornado, se não houver um. Sem isso, o teste continuaria e causaria erros por causa do ponteiro `nil`.

## Execute o teste

`carteira_test.go:61: erro resultado 'eita', erro esperado 'não é possível retirar: saldo insuficiente'`

## Escreva código o suficiente para fazer o teste passar

```go
func (c *Carteira) Retirar(quantidade Bitcoin) error {

    if quantidade > c.saldo {
        return errors.New("não é possível retirar: saldo insuficiente")
    }

    c.saldo -= quantidade
    return nil
}
```

## Refatoração

Temos duplicação da mensagem de erro tanto no código de teste quanto no código de `Retirar`.

Seria chato se o teste falhasse por alguém ter mudado a mensagem do erro e é muito detalhe para o nosso teste. Nós não *necessariamente* nos importamos qual mensagem é exatamente, apenas que algum tipo de erro significativo sobre a função é retornado dada uma certa condição.

Em Go, erros são valores, então podemos refatorar isso para ser uma variável e termos apenas uma fonte da verdade.

```go
var ErroSaldoInsuficiente = errors.New("não é possível retirar: saldo insuficiente")

func (c *Carteira) Retirar(quantidade Bitcoin) error {

    if quantidade > c.saldo {
        return ErroSaldoInsuficiente
    }

    c.saldo -= quantidade
    return nil
}
```

A palavra-chave `var` no escopo do arquivo nos permite definir valores globais para o pacote.

Esta é uma mudança positiva, pois agora nossa função `Retirar` parece mais limpa.

Agora, podemos refatorar nosso código para usar este valor ao invés de uma string específica.

```go
func TestCarteira(t *testing.T) {
	t.Run("Depositar", func(t *testing.T) {
		carteira := Carteira{}
		carteira.Depositar(Bitcoin(10))

		confirmaSaldo(t, carteira, Bitcoin(10))
	})

	t.Run("Retirar com saldo suficiente", func(t *testing.T) {
		carteira := Carteira{Bitcoin(20)}
		erro := carteira.Retirar(Bitcoin(10))

		confirmaSaldo(t, carteira, Bitcoin(10))
	})

	t.Run("Retirar com saldo insuficiente", func(t *testing.T) {
		saldoInicial := Bitcoin(20)
		carteira := Carteira{saldoInicial}
		erro := carteira.Retirar(Bitcoin(100))

		confirmaSaldo(t, carteira, saldoInicial)
		confirmaErro(t, erro, ErroSaldoInsuficiente)
	})
}

func confirmaSaldo(t *testing.T, carteira Carteira, esperado Bitcoin) {
	t.Helper()
	resultado := carteira.Saldo()

	if resultado != esperado {
		t.Errorf("resultado %s, esperado %s", resultado, esperado)
	}
}

func confirmaErro(t *testing.T, resultado error, esperado error) {
	t.Helper()
	if resultado == nil {
		t.Fatal("esperava um erro, mas nenhum ocorreu")
	}

	if resultado != esperado {
		t.Errorf("erro resultado %s, erro esperado %s", resultado, esperado)
	}
}
```

Agora está mais fácil dar continuidade ao nosso teste.

Nós apenas movemos os métodos auxiliares para fora da função principal de teste. Logo, quando alguém abrir o arquivo, começará lendo nossas asserções primeiro ao invés desses métodos auxiliares.

Outra propriedade útil de testes é que eles nos ajudam a entender o uso *real* do nosso código e assim podemos fazer códigos mais compreensivos. Podemos ver aqui que um desenvolvedor pode simplesmente chamar nosso código e fazer uma comparação de igualdade a `ErroSaldoInsuficiente`, e então agir de acordo.

### Erros não verificados

Embora o compilador do Go ajude bastante, há coisas que você pode acabar errando e o tratamento de erro pode se tornar complicado.

Há um cenário que nós não testamos. Para descobri-lo, execute o comando a seguir no terminal para instalar o `errcheck`, um dos muitos linters disponíveis em Go.

`go get -u github.com/kisielk/errcheck`

Então, dentro do diretório do seu código, execute `errcheck .`.

Você deve receber algo assim:

`carteira_test.go:17:18: carteira.Retirar(Bitcoin(10))`

O que isso está nos dizendo é que não verificamos o erro sendo retornado naquela linha de código. Aquela linha de código, no meu computador, corresponde para o nosso cenário normal de retirada, porque não verificamos que se `Retirar` é bem sucedido quando um erro *não* é retornado.

Aqui está o código de teste final que resolve isto.

```go
func TestCarteira(t *testing.T) {
	t.Run("Depositar", func(t *testing.T) {
		carteira := Carteira{}
		carteira.Depositar(Bitcoin(10))

		confirmaSaldo(t, carteira, Bitcoin(10))
	})

	t.Run("Retirar com saldo suficiente", func(t *testing.T) {
		carteira := Carteira{Bitcoin(20)}
		erro := carteira.Retirar(Bitcoin(10))

		confirmaSaldo(t, carteira, Bitcoin(10))
		confirmaErroInexistente(t, erro)
	})

	t.Run("Retirar com saldo insuficiente", func(t *testing.T) {
		saldoInicial := Bitcoin(20)
		carteira := Carteira{saldoInicial}
		erro := carteira.Retirar(Bitcoin(100))

		confirmaSaldo(t, carteira, saldoInicial)
		confirmaErro(t, erro, ErroSaldoInsuficiente)
	})
}

func confirmaSaldo(t *testing.T, carteira Carteira, esperado Bitcoin) {
	t.Helper()
	resultado := carteira.Saldo()

	if resultado != esperado {
		t.Errorf("resultado %s, esperado %s", resultado, esperado)
	}
}

func confirmaErroInexistente(t *testing.T, resultado error) {
	t.Helper()
	if resultado != nil {
		t.Fatal("erro inesperado recebido")
	}
}

func confirmaErro(t *testing.T, resultado error, esperado error) {
	t.Helper()
	if resultado == nil {
		t.Fatal("esperava um erro, mas nenhum ocorreu")
	}

	if resultado != esperado {
		t.Errorf("erro resultado %s, erro esperado %s", resultado, esperado)
	}
}
```

## Resumo

### Ponteiros

* Go copia os valores quando são passados para funções/métodos. Então, se estiver escrevendo uma função que precise mudar o estado, você precisará de um ponteiro para o valor que você quer mudar.
* O fato de que Go pega um cópia dos valores é muito útil na maior parte do tempo, mas às vezes você não vai querer que o seu sistema faça cópia de alguma coisa. Nesse caso, você precisa passar uma referência. Podemos, por exemplo, ter dados muito grandes, ou coisas que você talvez pretenda ter apenas uma instância (como conexões a banco de dados).

### nil

* Ponteiros podem ser `nil`.
* Quando uma função retorna um ponteiro para algo, você precisa ter certeza de verificar se ele é `nil` ou isso vai gerar uma exceção em tempo de execução, já que o compilador não te consegue te ajudar nesses casos.
* Útil para quando você quer descrever um valor que pode estar faltando.

### Erros

* Erros são a forma de sinalizar falhas na execução de uma função/método.
* Analisando nossos testes, concluímos que buscar por uma string em um erro poderia resultar em um teste não muito confiável. Então, refatoramos para usar um valor significativo, que resultou em um código mais fácil de ser testado e concluímos que também seria mais fácil para usuários de nossa API.
* Este não é o fim do assunto de tratamento de erros. Você pode fazer coisas mais sofisticadas, mas esta é apenas uma introdução. Capítulos posteriores vão abordar mais estratégias.
* [Não somente verifique os erros, trate-os graciosamente](https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully)

### Crie novos tipos a partir de existentes

* Útil para adicionar domínios mais específicos a valores
* Permite implementar interfaces

Ponteiros e erros são uma grande parte de escrita em Go que você precisa estar confortável. Por sorte, *na maioria das vezes* o compilador irá ajudar se você fizer algo errado. É só tomar um tempinho lendo a mensagem de erro.


# Maps

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/maps)

Em [arrays e slices](/aprenda-go-com-testes/main/primeiros-passos-com-go/arrays-e-slices), vimos como armazenar valores em ordem. Agora, vamos descobrir uma forma de armazenar itens por uma `key` (chave) e procurar por ela rapidamente.

Maps te permitem armazenar itens de forma parecida com a de um dicionário. Você pode pensar na `chave` como a palavra e o `valor` como a definição. E tem forma melhor de aprender sobre maps do que criar seu próprio dicionário?

Primeiro, vamos presumir que já temos algumas palavras com suas definições no dicionário. Se procurarmos por uma palavra, o dicionário deve retornar sua definição.

## Escreva o teste primeiro

Em `dicionario_test.go`

```go
package main

import "testing"

func TestBusca(t *testing.T) {
    dicionario := map[string]string{"teste": "isso é apenas um teste"}

    resultado := Busca(dicionario, "teste")
    esperado := "isso é apenas um teste"

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s', dado '%s'", resultado, esperado, "test")
    }
}
```

Declarar um map é bem parecido com declarar um array. A diferença é que começa com a palavra-chave `map` e requer dois tipos. O primeiro é o tipo da chave, que é escrito dentro de `[]`. O segundo é o tipo do valor, que vai logo após o `[]`.

O tipo da chave é especial. Só pode ser um tipo comparável, porque sem a habilidade de dizer se duas chaves são iguais, não temos como ter certeza de que estamos obtendo o valor correto. Tipos comparáveis são explicados com detalhes na [especificação da linguagem](https://golang.org/ref/spec#Comparison_operators) (em inglês).

O tipo do valor, por outro lado, pode ser o tipo que quiser. Pode até ser outro map.

O restante do teste já deve ser familiar para você.

## Execute o teste

Ao executar `go test`, o compilador vai falhar com `./dicionario_test.go:8:9: undefined: Busca`.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Em `dicionario.go`:

```go
package main

func Busca(dicionario map[string]string, palavra string) string {
    return ""
}
```

Agora seu teste vai falhar com uma *mensagem de erro clara*:

`dicionario_test.go:12: resultado '', esperado 'isso é apenas um teste', dado 'teste'`.

## Escreva código o suficiente para fazer o teste passar

```go
func Busca(dicionario map[string]string, palavra string) string {
    return dicionario[palavra]
}
```

Obter um valor de um map é igual a obter um valor de um array: `map[chave]`.

## Refatoração

```go
func TestBusca(t *testing.T) {
    dicionario := map[string]string{"teste": "isso é apenas um teste"}

    resultado := Busca(dicionario, "teste")
    esperado := "isso é apenas um teste"

    comparaStrings(t, resultado, esperado)
}

func comparaStrings(t *testing.T, resultado, esperado string) {
    t.Helper()

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s', dado '%s'", resultado, esperado, "teste")
    }
}
```

Decidi criar um helper `comparaStrings` para tornar a implementação mais genérica.

### Usando um tipo personalizado

Podemos melhorar o uso do nosso dicionário criando um novo tipo baseado no map e transformando a `Busca` em um método.

Em `dicionario_test.go`:

```go
func TestBusca(t *testing.T) {
    dicionario := Dicionario{"teste": "isso é apenas um teste"}

    resultado := dicionario.Busca("teste")
    esperado := "isso é apenas um teste"

    comparaStrings(t, resultado, esperado)
}
```

Começamos a usar o tipo `Dicionario`, que ainda não definimos. Depois disso, chamamos `Busca` da instância de `Dicionario`.

Não precisamos mudar o `comparaStrings`.

Em `dicionario.go`:

```go
type Dicionario map[string]string

func (d Dicionario) Busca(palavra string) string {
    return d[palavra]
}
```

Aqui criamos um tipo `Dicionario` que trabalha em cima da abstração de `map`. Com o tipo personalizado definido, podemos criar o método `Busca`.

## Escreva o teste primeiro

A busca básica foi bem fácil de implementar, mas o que acontece se passarmos uma palavra que não está no nosso dicionário?

Com o código atual, não recebemos nada de volta. Isso é bom porque o programa continua a ser executado, mas há uma abordagem melhor. A função pode reportar que a palavra não está no dicionário. Dessa forma, o usuário não fica se perguntando se a palavra não existe ou se apenas não existe definição para ela (isso pode não parecer tão útil para um dicionário. No entanto, é um caso que pode ser essencial em outros casos de uso).

```go
func TestBusca(t *testing.T) {
    dicionario := Dicionario{"teste": "isso é apenas um teste"}

    t.Run("palavra conhecida", func(t *testing.T) {
        resultado, _ := dicionario.Busca("teste")
        esperado := "isso é apenas um teste"

        comparaStrings(t, resultado, esperado)
    })

    t.Run("palavra desconhecida", func(t *testing.T) {
        _, err := dicionario.Busca("desconhecida")

        if err == nil {
            t.Fatal("é esperado que um erro seja obtido.")
        }
    })
}
```

A forma de lidar com esse caso no Go é retornar um segundo argumento que é do tipo `Error`.

Erros podem ser convertidos para uma string com o método `.Error()`, o que podemos fazer quando passarmos para a asserção. Também estamos protegendo o `comparaStrings` com `if` para certificar que não chamemos `.Error()` quando o erro for `nil`.

## Execute o teste

Isso não vai compilar.

`./dicionario_test.go:18:10: assignment mismatch: 2 variables but 1 values`

`incompatibilidade de atribuição: 2 variáveis, mas 1 valor`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (d Dicionario) Busca(palavra string) (string, error) {
    return d[palavra], nil
}
```

Agora seu teste deve falhar com uma mensagem de erro muito mais clara.

`dicionario_test.go:22: expected to get an error.`

`erro esperado.`

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Busca(palavra string) (string, error) {
    definicao, existe := d[palavra]
    if !existe {
        return "", errors.New("não foi possível encontrar a palavra que você procura")
    }

    return definicao, nil
}
```

Para fazê-lo passar, estamos usando uma propriedade interessante ao percorrer o map. Ele pode retornar dois valores. O segundo valor é uma boleana que indica se a chave foi encontrada com sucesso.

Essa propriedade nos permite diferenciar entre uma palavra que não existe e uma palavra que simplesmente não tem uma definição.

## Refatoração

```go
var ErrNaoEncontrado = errors.New("não foi possível encontrar a palavra que você procura")

func (d Dicionario) Busca(palavra string) (string, error) {
    definicao, existe := d[palavra]
    if !existe {
        return "", ErrNaoEncontrado
    }

    return definicao, nil
}
```

Podemos nos livrar do "erro mágico" na nossa função de `Busca` extraindo-o para dentro de uma variável. Isso também nos permite ter um teste melhor.

```go
t.Run("palavra desconhecida", func(t *testing.T) {
    _, resultado := dicionario.Busca("desconhecida")

    comparaErro(t, resultado, ErrNaoEncontrado)
})

func comparaErro(t *testing.T, resultado, esperado error) {
    t.Helper()

    if resultado != esperado {
        t.Errorf("resultado erro '%s', esperado '%s'", resultado, esperado)
    }
}
```

Conseguimos simplificar nosso teste criando um novo helper e começando a usar nossa variável `ErrNaoEncontrado` para que nosso teste não falhe se mudarmos o texto do erro no futuro.

## Escreva o teste primeiro

Temos uma ótima maneira de buscar no dicionário. No entanto, não temos como adicionar novas palavras nele.

```go
func TestAdiciona(t *testing.T) {
    dicionario := Dicionario{}
    dicionario.Adiciona("teste", "isso é apenas um teste")

    esperado := "isso é apenas um teste"
    resultado, err := dicionario.Busca("teste")
    if err != nil {
        t.Fatal("não foi possível encontrar a palavra adicionada:", err)
    }

    if esperado != resultado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

Nesse teste, estamos utilizando nossa função `Busca` para tornar a validação do dicionário um pouco mais fácil.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Em `dicionario.go`

```go
func (d Dicionario) Adiciona(palavra, definicao string) {
}
```

Agora seu teste deve falhar.

```bash
dicionario_test.go:31: deveria ter encontrado palavra adicionada: não foi possível encontrar a palavra que você procura
```

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Adiciona(palavra, definicao string) {
    d[palavra] = definicao
}
```

Adicionar coisas a um map também é bem semelhante a um array. Você só precisar especificar uma chave e definir qual é seu valor.

### Tipos Referência

Uma propriedade interessante dos maps é que você pode modificá-los sem passá-los como ponteiro. Isso é porque o `map` é um tipo referência. Isso significa que ele contém uma referência à estrutura de dado que estamos utilizando, assim como um ponteiro. Logo, quando criamos passamos o map como parâmetro, estamos alterando o map original e não sua cópia. A estrutura de dados utilizada é uma `tabela de dispersão` ou `mapa de hash`, e você pode ler mais sobre [aqui](https://pt.wikipedia.org/wiki/Tabela_de_dispers%C3%A3o).

É muito bom ter o map como referência, porque não importa o tamanho do map, só vai haver uma cópia.

Um conceito que os tipos referência apresentam é que maps podem ser um valor `nil`. Um map `nil` se comporta como um map vazio durante a leitura, mas tentar inserir coisas em um map `nil` gera um panic em tempo de execução. Você pode saber mais sobre maps [aqui](https://blog.golang.org/go-maps-in-action) (em inglês).

Além disso, você nunca deve inicializar um map vazio, como:

```go
var m map[string]string
```

Ao invés disso, você pode inicializar um map vazio como fizemos lá em cima, ou usando a palavra-chave `make` para criar um map para você:

```go
dicionario = map[string]string{}

// OU

dicionario = make(map[string]string)
```

Ambas as abordagens criam um `hash map` vazio e apontam um `dicionario` para ele. Assim, nos certificamos que você nunca vai obter um panic em tempo de execução.

## Refatoração

Não há muito para refatorar na nossa implementação, mas podemos simplificar o teste.

```go
func TestAdiciona(t *testing.T) {
    dicionario := Dicionario{}
    palavra := "teste"
    definicao := "isso é apenas um teste"

    dicionario.Adiciona(palavra, definicao)

    comparaDefinicao(t, dicionario, palavra, definicao)
}

func comparaDefinicao(t *testing.T, dicionario Dicionario, palavra, definicao string) {
    t.Helper()

    resultado, err := dicionario.Busca(palavra)
    if err != nil {
        t.Fatal("deveria ter encontrado palavra adicionada:", err)
    }

    if definicao != resultado {
        t.Errorf("resultado '%s',  esperado '%s'", resultado, definicao)
    }
}
```

Criamos variáveis para palavra e definição e movemos a comparação da definição para sua própria função auxiliar.

Nosso `Adiciona` está bom. No entanto, não consideramos o que acontece quando o valor que estamos tentando adicionar já existe!

O map não vai mostrar um erro se o valor já existe. Ao invés disso, ele vai sobrescrever o valor com o novo recebido. Isso pode ser conveniente na prática, mas torna o nome da nossa função muito menos preciso. `Adiciona` não deve modificar valores existentes. Só deve adicionar palavras novas ao nosso dicionário.

## Escreva o teste primeiro

```go
func TestAdiciona(t *testing.T) {
    t.Run("palavra nova", func(t *testing.T) {
        dicionario := Dicionario{}
        palavra := "teste"
        definicao := "isso é apenas um teste"

        err := dicionario.Adiciona(palavra, definicao)

        comparaErro(t, err, nil)
        comparaDefinicao(t, dicionario, palavra, definicao)
    })

    t.Run("palavra existente", func(t *testing.T) {
        palavra := "teste"
        definicao := "isso é apenas um teste"
        dicionario := Dicionario{palavra: definicao}
        err := dicionario.Adiciona(palavra, "teste novo")

        comparaErro(t, err, ErrPalavraExistente)
        comparaDefinicao(t, dicionario, palavra, definicao)
    })
}
```

Para esse teste, fizemos `Adiciona` devolver um erro, que estamos validando com uma nova variável de erro, `ErrPalavraExistente`. Também modificamos o teste anterior para verificar um erro `nil`.

## Execute o teste

Agora o compilador vai falhar porque não estamos devolvendo um valor para `Adiciona`.

```bash
./dicionario_test.go:30:13: dicionario.Adiciona(palavra, definicao) used as value
./dicionario_test.go:41:13: dicionario.Adiciona(palavra, "teste novo") used as value
```

`usado como valor`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Em `dicionario.go`:

```go
var (
    ErrNaoEncontrado = errors.New("não foi possível encontrar a palavra que você procura")
    ErrPalavraExistente = errors.New("não é possível adicionar a palavra pois ela já existe")
)

func (d Dicionario) Adiciona(palavra, definicao string) error {
    d[palavra] = definicao
    return nil
}
```

Agora temos mais dois erros. Ainda estamos modificando o valor e retornando um erro `nil`.

```bash
dicionario_test.go:43: resultado erro '%!s(<nil>)', esperado 'não é possível adicionar a palavra pois ela já existe'
dicionario_test.go:44: resultado 'teste novo', esperado 'isso é apenas um teste'
```

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Adiciona(palavra, definicao string) error {
    _, err := d.Busca(palavra)
    switch err {
    case ErrNaoEncontrado:
        d[palavra] = definicao
    case nil:
        return ErrPalavraExistente
    default:
        return err

    }

    return nil
}
```

Aqui estamos usando a declaração `switch` para coincidir com o erro. Usar o `switch` dessa forma dá uma segurança a mais, no caso de `Busca` retornar um erro diferente de `ErrNaoEncontrado`.

## Refatoração

Não temos muito o que refatorar, mas já que nossos erros estão aumentando, podemos fazer algumas modificações.

```go
const (
    ErrNaoEncontrado = ErrDicionario("não foi possível encontrar a palavra que você procura")
    ErrPalavraExistente = ErrDicionario("não é possível adicionar a palavra pois ela já existe")
)

type ErrDicionario string

func (e ErrDicionario) Error() string {
    return string(e)
}
```

Tornamos os erros constantes; para isso, tivemos que criar nosso próprio tipo `ErrDicionario` que implementa a interface `error`. Você pode ler mais sobre nesse [artigo excelente escrito por Dave Cheney](https://dave.cheney.net/2016/04/07/constant-errors) (em inglês). Resumindo, isso torna os erros mais reutilizáveis e imutáveis.

Agora, vamos criar uma função que `Atualiza` a definição de uma palavra.

## Escreva o teste primeiro

```go
func TestUpdate(t *testing.T) {
    palavra := "teste"
    definicao := "isso é apenas um teste"
    dicionario := Dicionario{palavra: definicao}
    novaDefinicao := "nova definição"

    dicionario.Atualiza(palavra, novaDefinicao)

    comparaDefinicao(t, dicionario, palavra, novaDefinicao)
}
```

`Atualiza` é bem parecido com `Adiciona` e será nossa próxima implementação.

## Execute o teste

```bash
./dicionario_test.go:53:2: dicionario.Atualiza undefined (type Dicionario has no field or method Atualiza)
```

`dicionario.Atualiza não definido (tipo Dicionario não tem nenhum campo ou método chamado Atualiza`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Já sabemos como lidar com um erro como esse. Precisamos definir nossa função.

```go
func (d Dicionario) Atualiza(palavra, definicao string) {}
```

Feito isso, somos capazes de ver o que precisamos para mudar a definição da palavra.

```bash
dicionario_test.go:55: resultado 'isso é apenas um teste', esperado 'nova definição'
```

## Escreva código o suficiente para fazer o teste passar

Já vimos como fazer essa implementação quando corrigimos o problema com `Adiciona`. Logo, vamos implementar algo bem parecido com `Adiciona`.

```go
func (d Dicionario) Atualiza(palavra, definicao string) {
    d[palavra] = definicao
}
```

Não é necessário fazer refatorar nada, já que foi uma mudança simples. No entanto, agora temos o mesmo problema com `Adiciona`. Se passarmos uma palavra nova, `Atualiza` vai adicioná-la no dicionário.

## Escreva o teste primeiro

```go
    t.Run("palavra existente", func(t *testing.T) {
        palavra := "teste"
        definicao := "isso é apenas um teste"
        novaDefinicao := "nova definição"
        dicionario := Dicionario{palavra: definicao}
        err := dicionario.Atualiza(palavra, novaDefinicao)

        comparaErro(t, err, nil)
        comparaDefinicao(t, dicionario, palavra, novaDefinicao)
    })

    t.Run("palavra nova", func(t *testing.T) {
        palavra := "teste"
        definicao := "isso é apenas um teste"
        dicionario := Dicionario{}

        err := dicionario.Atualiza(palavra, definicao)

        comparaErro(t, err, ErrPalavraInexistente)
    })
```

Criamos um outro tipo de erro para quando a palavra não existe. Também modificamos o `Atualiza` para retornar um valor `error`.

## Execute o teste

```bash
./dicionario_test.go:53:16: dicionario.Atualiza(palavra, "teste novo") used as value
./dicionario_test.go:64:16: dicionario.Atualiza(palavra, definicao) used as value
./dicionario_test.go:66:23: undefined: ErrPalavraInexistente
```

Agora recebemos três erros, mas sabemos como lidar com eles.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
const (
    ErrNaoEncontrado = ErrDicionario("não foi possível encontrar a palavra que você procura")
    ErrPalavraExistente = ErrDicionario("não é possível adicionar a palavra pois ela já existe")
    ErrPalavraInexistente = ErrDicionario("não foi possível atualizar a palavra pois ela não existe")
)

func (d Dicionario) Atualiza(palavra, definicao string) error {
    d[palavra] = definicao
    return nil
}
```

Adicionamos nosso próprio tipo erro e retornamos um erro `nil`.

Com essas mudanças, agora temos um erro muito mais claro:

```bash
dicionario_test.go:66: resultado erro '%!s(<nil>)', esperado 'não foi possível atualizar a palavra pois ela não existe'
```

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Atualiza(palavra, definicao string) error {
    _, err := d.Busca(palavra)
    switch err {
    case ErrNaoEncontrado:
        return ErrPalavraInexistente
    case nil:
        d[palavra] = definicao
    default:
        return err

    }

    return nil
}
```

Essa função é quase idêntica à `Adiciona`, com exceção de que trocamos quando atualizamos o `dicionario` e quando retornamos um erro.

### Nota sobre a declaração de um novo erro para Atualiza

Poderíamos reutilizar `ErrNaoEncontrado` e não criar um novo erro. No entanto, geralmente é melhor ter um erro preciso para quando uma atualização falhar.

Ter erros específicos te dá mais informação sobre o que deu errado. Segue um exemplo em uma aplicação web:

> Você pode redirecionar o usuário quando o `ErrNaoEncontrado` é encontrado, mas mostrar uma mensagem de erro só quando `ErrPalavraInexistente` é encontrado.

Agora, vamos criar uma função que `Deleta` uma palavra no dicionário.

## Escreva o teste primeiro

```go
func TestDeleta(t *testing.T) {
    palavra := "teste"
    dicionario := Dicionario{palavra: "definição de teste"}

    dicionario.Deleta(palavra)

    _, err := dicionario.Busca(palavra)
    if err != ErrNaoEncontrado {
        t.Errorf("espera-se que '%s' seja deletado", palavra)
    }
}
```

Nosso teste cria um `Dicionario` com uma palavra e depois verifica se a palavra foi removida.

## Execute o teste

Executando `go test` obtemos:

```bash
./dicionario_test.go:74:6: dicionario.Deleta undefined (type Dicionario has no field or method Deleta)
```

`dicionario.Deleta não definido (tipo Dicionario não tem campo ou método Deleta)`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (d Dicionario) Deleta(palavra string) {

}
```

Depois que adicionamos isso, o teste nos diz que não estamos deletando a palavra.

```bash
dicionario_test.go:78: espera-se que 'teste' seja deletado
```

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Deleta(palavra string) {
    delete(d, palavra)
}
```

Go tem uma função nativa chamada `delete` que funciona em maps. Ela leva dois argumentos: o primeiro é o map e o segundo é a chave a ser removida.

A função `delete` não retorna nada, e baseamos nosso método `Deleta` nesse conceito. Já que deletar um valor não tem nenhum efeito, diferentemente dos nossos métodos `Atualiza` e `Adiciona`, não precisamos complicar a API com erros.

## Resumo

Nessa seção, falamos sobre muita coisa. Criamos uma API CRUD (Criar, Ler, Atualizar e Deletar) completa para nosso dicionário. No decorrer do processo, aprendemos como:

* Criar maps
* Buscar por itens em maps
* Adicionar novos itens aos maps
* Atualizar itens em maps
* Deletar itens de um map
* Aprendemos mais sobre erros
  * Como criar erros que são constantes
  * Escrever encapsuladores de erro


# Injeção de dependência

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/injecao-de-dependencia)

Presume-se que você tenha lido a seção de `structs` antes, já que será necessário saber um pouco sobre interfaces para entender este capítulo.

Há muitos mal entendidos relacionados à injeção de dependência na comunidade de programação. Se tudo der certo, esse guia vai te mostrar que:

* Você não precisa de uma framework
* Não torna seu design complexo demais
* Facilita seus testes
* Permite que você escreva funções ótimas para propósitos diversos.

Queremos criar uma função que cumprimenta alguém, assim como a que fizemos no capítulo [Olá, mundo](/aprenda-go-com-testes/main/primeiros-passos-com-go/ola-mundo), mas dessa vez vamos testar o *print de verdade*.

Para recapitular, a função era parecida com isso:

```go
func Cumprimenta(nome string) {
    fmt.Printf("Olá, %s", nome)
}
```

Mas como podemos testar isso? Chamar `fmt.Printf` imprime na saída, o que torna a captura com a ferramenta de testes bem difícil para nós.

O que precisamos fazer é sermos capazes de **injetar** (que é só uma palavra chique para passar) a dependência de impressão.

**Nossa função não precisa se preocupar com** ***onde*** **ou** ***como*** **a impressão acontece, então vamos aceitar uma** ***interface*** **ao invés de um tipo concreto.**

Se fizermos isso, podemos mudar a implementação para imprimir algo que controlamos para poder testá-lo. Na "vida real", você iria injetar em algo que escreve na saída.

Se dermos uma olhada no código fonte do `fmt.Printf`, podemos ver uma forma de começar:

```go
// Printf retorna o número de bytes escritos e algum erro de escrita encontrado.
func Printf(format string, a ...interface{}) (n int, err error) {
    return Fprintf(os.Stdout, format, a...)
}
```

Interessante! Por baixo dos panos, o `Printf` só chama o `Fprintf` passando o `os.Stdout`.

O que exatamente *é* um `os.Stdout`? O que o `Fprintf` espera que passe para ele como primeiro argumento?

```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
}
```

Um `io.Writer`:

```go
type Writer interface {
    Write(p []byte) (n int, err error)
}
```

Quanto mais você escreve código em Go, mais vai perceber que essa interface aparece bastante, pois é uma ótima interface de uso geral para "colocar esses dados em algum lugar".

Logo, sabemos que por baixo dos panos estamos usando o `Writer` para enviar nosso cumprimento para algum lugar. Vamos usar essa abstração existente para tornar nosso código testável e mais reutilizável.

## Escreva o teste primeiro

```go
func TestCumprimenta(t *testing.T) {
    buffer := bytes.Buffer{}
    Cumprimenta(&buffer, "Chris")

    resultado := buffer.String()
    esperado := "Olá, Chris"

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

O tipo `buffer` do pacote `bytes` implementa a interface `Writer`.

Logo, vamos utilizá-lo no nosso teste para enviá-lo como nosso `Writer` e depois podemos verificar o que foi escrito nele quando chamamos `Cumprimenta`.

## Execute o teste

O teste não vai compilar:

```bash
./id_test.go:10:7: too many arguments in call to Cumprimenta
    have (*bytes.Buffer, string)
    want (string)
```

```bash
./id_test.go:10:7: muitos argumentos na chamada de Cumprimenta
    obteve (*bytes.Buffer, string)
    esperado (string)
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

*Preste atenção no compilador* e corrija o problema.

```go
func Cumprimenta(escritor io.Writer, nome string) {
    fmt.Printf("Olá, %s", nome)
}
```

`Olá, Chris id_test.go:16: resultado '', esperado 'Olá, Chris'`

O teste falha. Note que o nome está sendo impresso, mas está indo para a saída.

## Escreva código o suficiente para fazer o teste passar

Use o escritor para enviar o cumprimento para o buffer no nosso teste. Lembre-se que o `fmt.Fprintf` é parecido com o `fmt.Printf`, com a diferença de que leva um `Writer` em que a string é enviada, enquanto que o`fmt.Printf` redireciona para a saída por padrão.

```go
func Cumprimenta(escritor io.Writer, nome string) {
	fmt.Fprintf(escritor, "Olá, %s", nome)
}
```

Agora o teste vai passar.

## Refatoração

Antes, o compilador nos disse para passar um ponteiro para um `bytes.Buffer`. Isso está tecnicamente correto, mas não é muito útil.

Para demonstrar isso, tente utilizar a função `Cumprimenta` em uma aplicação Go onde queremos que imprima na saída.

```go
func main() {
    Cumprimenta(os.Stdout, "Elodie")
}
```

`./id.go:14:7: cannot use os.Stdout (type *os.File) as type *bytes.Buffer in argument to Cumprimenta`

`não é possível utilizar os.Stdout (tipo *os.File) como tipo *bytes.Buffer no argumento para Cumprimenta`

Como discutimos antes, o `fmt.Fprintf` te permite passar um `io.Writer`, que sabemos que o `os.Stdout` e `bytes.Buffer` implementam.

Se mudarmos nosso código para usar uma interface de propósito mais geral, podemos usá-la tanto nos testes quanto na nossa aplicação.

```go
package main

import (
    "fmt"
    "os"
    "io"
)

func Cumprimenta(escritor io.Writer, nome string) {
    fmt.Fprintf(escritor, "Olá, %s", nome)
}

func main() {
    Cumprimenta(os.Stdout, "Elodie")
}
```

## Mais sobre io.Writer

Quais outros lugares podemos escrever dados usando `io.Writer`? Para qual propósito geral nossa função `Cumprimenta` é feita?

### A internet

Execute o seguinte:

```go
package main

import (
    "fmt"
    "io"
    "net/http"
)

func Cumprimenta(escritor io.Writer, nome string) {
    fmt.Fprintf(escritor, "Olá, %s", nome)
}

func HandlerMeuCumprimento(w http.ResponseWriter, r *http.Request) {
    Cumprimenta(w, "mundo")
}

func main() {
    err := http.ListenAndServe(":5000", http.HandlerFunc(HandlerMeuCumprimento))

    if err != nil {
        fmt.Println(err)
    }
}
```

Execute o programa e vá para <http://localhost:5000>. Você verá sua função de cumprimento ser utilizada.

Falaremos sobre servidores HTTP em um próximo capítulo, então não se preocupe muito com os detalhes.

Quando se cria um handler HTTP, você recebe um `http.ResponseWriter` e o `http.Request` que é usado para fazer a requisição. Quando implementa seu servidor, você *escreve* sua resposta usando o escritor.

Você deve ter adivinhado que o `http.ResponseWriter` também implementa o `io.Writer` e é por isso que podemos reutilizar nossa função `Cumprimenta` dentro do nosso handler.

## Resumo

Nossa primeira rodada de código não foi fácil de testar porque escrevemos dados em algum lugar que não podíamos controlar.

*Graças aos nossos testes*, refatoramos o código para que pudéssemos controlar para *onde* os dados eram escritos **injetando uma dependência** que nos permitiu:

* **Testar nosso código**: se você não consegue testar uma função *de forma simples*, geralmente é porque dependências estão acopladas em uma função *ou* estado global. Se você tem um pool de conexão global da base de dados, por exemplo, é provável que seja difícil testar e vai ser lento para ser execudado. A injeção de dependência te motiva a injetar em uma dependência da base de dados (através de uma interface), para que você possa criar um mock com algo que você possa controlar nos seus testes.
* *Separar nossas preocupações*, desacoplando *onde os dados vão* de *como gerá-los*. Se você já achou que um método/função tem responsabilidades demais (gerando dados *e* escrevendo na base de dados? Lidando com requisições HTTP *e* aplicando lógica a nível de domínio?), a injeção de dependência provavelmente será a ferramenta que você precisa.
* **Permitir que nosso código seja reutilizado em contextos diferentes**: o primeiro contexto "novo" do nosso código pode ser usado dentro dos testes. No entanto, se alguém quiser testar algo novo com nossa função, a pessoa pode injetar suas próprias dependências.

### Mas e o mock? Ouvi falar que precisa disso para trabalhar com injeção de dependência e que também é do demonho

Vamos falar mais sobre mocks depois (e não é do demonho). Você mocka para substituir coisas reais que você injeta com uma versão falsa que você pode controlar e examinar nos seus testes. No entanto, no nosso caso a biblioteca padrão já tinha algo pronto para usarmos.

### A biblioteca padrão do Go é muito boa, leve um tempo para estudá-la

Ao termos familiaridade com a interface `io.Writer`, somos capazes de usar `bytes.Buffer` no nosso teste como nosso `Writer` para que depois possamos usar outros `Writer` da biblioteca padrão para usar na nossa função em uma aplicação de linha de comando ou em um servidor web.

Quanto mais familiar você for com a biblioteca padrão, mais vai ver essas interfaces de propósito geral que você pode reutilizar no seu próprio código para tornar o software reutilizável em vários contextos diferentes.

Esse exemplo teve grande influência de um capítulo de [A Linguagem de Programação Go](https://www.amazon.com.br/dp/8575225464/ref=cm_sw_r_tw_dp_U_x_0HIqDbYP7VSN5). Logo, se gostou, vá adquiri-lo!


# Mocks

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/mocks)

Te pediram para criar um programa que conta a partir de 3, imprimindo cada número em uma linha nova (com um segundo de intervalo entre cada uma) e quando chega a zero, imprime "Vai!" e sai.

```
3
2
1
Vai!
```

Vamos resolver isso escrevendo uma função chamada `Contagem` que vamos colocar dentro de um programa `main` e se parecer com algo assim:

```go
package main

func main() {
    Contagem()
}
```

Apesar de ser um programa simples, para testá-lo completamente vamos precisar, como de costume, de uma abordagem *iterativa* e *orientada a testes*.

Mas o que quero dizer com iterativa? Precisamos ter certeza de que tomamos os menores passos que pudermos para ter um *software* útil.

Não queremos passar muito tempo com código que vai funcionar hora ou outra após alguma implementação mirabolante, porque é assim que os desenvolvedores caem em armadilhas. **É importante ser capaz de dividir os requerimentos da menor forma que conseguir para você ter um** ***software funcionando*****.**

Podemos separar essa tarefa da seguinte forma:

* Imprimir 3
* Imprimir de 3 para Vai!
* Esperar um segundo entre cada linha

## Escreva o teste primeiro

Nosso software precisa imprimir para a saída. Vimos como podemos usar a injeção de dependência para facilitar nosso teste na [seção anterior](/aprenda-go-com-testes/main/primeiros-passos-com-go/injecao-de-dependencia).

```go
func TestContagem(t *testing.T) {
    buffer := &bytes.Buffer{}

    Contagem(buffer)

    resultado := buffer.String()
    esperado := "3"

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

Se tiver dúvidas sobre o `buffer`, leia a [seção anterior](/aprenda-go-com-testes/main/primeiros-passos-com-go/injecao-de-dependencia) novamente.

Sabemos que nossa função `Contagem` precisa escrever dados em algum lugar e o `io.Writer` é a forma de capturarmos essa saída como uma interface em Go.

* Na `main`, vamos enviar o `os.Stdout` como parâmetro para nossos usuários verem a contagem regressiva impressa no terminal.
* No teste, vamos enviar o `bytes.Buffer` como parâmetro para que nossos testes possam capturar que dado está sendo gerado.

## Execute o teste

`./contagem_test.go:11:2: undefined: Contagem`

`indefinido: Contagem`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Defina `Contagem`:

```go
func Contagem() {}
```

Tente novamente:

```go
./contagem_test.go:11:11: too many arguments in call to Countdown
    have (*bytes.Buffer)
    want ()
```

`argumentos demais na chamada para Contagem`

O compilador está te dizendo como a assinatura da função deve ser, então é só atualizá-la.

```go
func Contagem(saida *bytes.Buffer) {}
```

`contagem_test.go:17: resultado '', esperado '3'`

Perfeito!

## Escreva código o suficiente para fazer o teste passar

```go
func Contagem(saida *bytes.Buffer) {
    fmt.Fprint(saida, "3")
}
```

Estamos usando `fmt.Fprint`, o que significa que ele recebe um `io.Writer` (como `*bytes.Buffer`) e envia uma `string` para ele. O teste deve passar.

## Refatoração

Agora sabemos que, apesar do `*bytes.Buffer` funcionar, seria melhor ter uma interface de propósito geral ao invés disso.

```go
func Contagem(saida io.Writer) {
    fmt.Fprint(saida, "3")
}
```

Execute os testes novamente e eles devem passar.

Só para finalizar, vamos colocar nossa função dentro da `main` para que possamos executar o software para nos assegurarmos de que estamos progredindo.

```go
package main

import (
    "fmt"
    "io"
    "os"
)

func Contagem(saida io.Writer) {
	fmt.Fprint(saida, "3")
}

func main() {
	Contagem(os.Stdout)
}
```

Execute o programa e surpreenda-se com seu trabalho.

Apesar de parecer simples, essa é a abordagem que recomendo para qualquer projeto. **Escolher uma pequena parte da funcionalidade e fazê-la funcionar do começo ao fim com apoio de testes.**

Depois, precisamos fazer o software imprimir 2, 1 e então "Vai!".

## Escreva o teste primeiro

Após investirmos tempo e esforço para fazer o principal funcionar, podemos iterar nossa solução com segurança e de forma simples. Não vamos mais precisar parar e executar o programa novamente para ter confiança de que ele está funcionando, desde que a lógica esteja testada.

```go
func TestContagem(t *testing.T) {
    buffer := &bytes.Buffer{}

    Contagem(buffer)

    resultado := buffer.String()
    esperado := `3
2
1
Vai!`
    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

A sintaxe de aspas simples é outra forma de criar uma `string`, mas te permite colocar coisas como linhas novas, o que é perfeito para nosso teste.

## Execute o teste

```bash
contagem_test.go:21: resultado '3', esperado '3
        2
        1
        Vai!'
```

## Escreva código o suficiente para fazer o teste passar

```go
func Contagem(saida io.Writer) {
    for i := 3; i > 0; i-- {
        fmt.Fprintln(saida, i)
    }
    fmt.Fprint(saida, "Vai!")
}
```

Usamos um laço `for` fazendo contagem regressiva com `i--` e depois `fmt.Fprintln` para imprimir a `saida` com nosso número seguro por um caracter de nova linha. Finalmente, usamos o `fmt.Fprint` para enviar "Vai!" no final.

## Refatoração

Não há muito para refatorar além de transformar alguns valores mágicos em constantes com nomes descritivos.

```go
const ultimaPalavra = "Vai!"
const inicioContagem = 3

func Contagem(saida io.Writer) {
    for i := inicioContagem; i > 0; i-- {
        fmt.Fprintln(saida, i)
    }
    fmt.Fprint(saida, ultimaPalavra)
}
```

Se executar o programa agora, você deve obter a saída desejada, mas não tem uma contagem regressiva dramática com as pausas de 1 segundo.

Go te permite obter isso com `time.Sleep`. Tente adicionar essa função ao seu código.

```go
func Contagem(saida io.Writer) {
    for i := inicioContagem; i > 0; i-- {
        time.Sleep(1 * time.Second)
        fmt.Fprintln(saida, i)
    }

    time.Sleep(1 * time.Second)
    fmt.Fprint(saida, ultimaPalavra)
}
```

Se você executar o programa, ele funciona conforme esperado.

## Mock

Os testes ainda vão passar e o software funciona como planejado, mas temos alguns problemas:

* Nossos testes levam 4 segundos para rodar.
  * Todo conteúdo gerado sobre desenvolvimento de software enfatiza a importância de loops de feedback rápidos.
  * **Testes lentos arruinam a produtividade do desenvolvedor**.
  * Imagine se os requerimentos ficam mais sofisticados, gerando a necessidade de mais testes. É viável adicionar 4s para cada teste novo de `Contagem`?
* Não testamos uma propriedade importante da nossa função.

Temos uma dependência no `Sleep` que precisamos extrair para podermos controlá-la nos nossos testes.

Se conseguirmos *mockar* o `time.Sleep`, podemos usar a *injeção de dependências* para usá-lo ao invés de um `time.Sleep` "de verdade", e então podemos **verificar as chamadas** para certificar de que estão corretas.

## Escreva o teste primeiro

Vamos definir nossa dependência como uma interface. Isso nos permite usar um Sleeper *de verdade* em `main` e um *sleeper spy* nos nossos testes. Usar uma interface na nossa função `Contagem` é essencial para isso e dá certa flexibilidade à função que a chamar.

```go
type Sleeper interface {
    Sleep()
}
```

Tomei uma decisão de design que nossa função `Contagem` não seria responsável por quanto tempo o sleep leva. Isso simplifica um pouco nosso código, pelo menos por enquanto, e significa que um usuário da nossa função pode configurar a duração desse tempo como preferir.

Agora precisamos criar um *mock* disso para usarmos nos nossos testes.

```go
type SleeperSpy struct {
    Chamadas int
}

func (s *SleeperSpy) Sleep() {
    s.Chamadas++
}
```

*Spies* (espiões) são um tipo de *mock* em que podemos gravar como uma dependência é usada. Eles podem gravar os argumentos definidos, quantas vezes são usados etc. No nosso caso, vamos manter o controle de quantas vezes `Sleep()` é chamada para verificá-la no nosso teste.

Atualize os testes para injetar uma dependência no nosso Espião e verifique se o sleep foi chamado 4 vezes.

```go
func TestContagem(t *testing.T) {
    buffer := &bytes.Buffer{}
    sleeperSpy := &SleeperSpy{}

    Contagem(buffer, sleeperSpy)

    resultado := buffer.String()
    esperado := `3
2
1
Vai!`

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }

    if sleeperSpy.Chamadas != 4 {
        t.Errorf("não houve chamadas suficientes do sleeper, esperado 4, resultado %d", sleeperSpy.Chamadas)
    }
}
```

## Execute o teste

```bash
too many arguments in call to Contagem
    have (*bytes.Buffer, *SpySleeper)
    want (io.Writer)
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Precisamos atualizar a `Contagem` para aceitar nosso `Sleeper`:

```go
func Contagem(saida io.Writer, sleeper Sleeper) {
    for i := inicioContagem; i > 0; i-- {
        time.Sleep(1 * time.Second)
        fmt.Fprintln(saida, i)
    }

    time.Sleep(1 * time.Second)
    fmt.Fprint(saida, ultimaPalavra)
}
```

Se tentar novamente, nossa `main` não vai mais compilar pelo mesmo motivo:

```
./main.go:26:11: not enough arguments in call to Contagem
    have (*os.File)
    want (io.Writer, Sleeper)
```

Vamos criar um sleeper *de verdade* que implementa a interface que precisamos:

```go
type SleeperPadrao struct {}

func (d *SleeperPadrao) Sleep() {
	time.Sleep(1 * time.Second)
}
```

Podemos usá-lo na nossa aplicação real, como:

```go
func main() {
    sleeper := &SleeperPadrao{}
    Contagem(os.Stdout, sleeper)
}
```

## Escreva código o suficiente para fazer o teste passar

Agora o teste está compilando, mas não passando. Isso acontece porque ainda estamos chamando o `time.Sleep` ao invés da injetada. Vamos arrumar isso.

```go
func Contagem(saida io.Writer, sleeper Sleeper) {
    for i := inicioContagem; i > 0; i-- {
        sleeper.Sleep()
        fmt.Fprintln(saida, i)
    }

    sleeper.Sleep()
    fmt.Fprint(saida, ultimaPalavra)
}
```

O teste deve passar sem levar 4 segundos.

### Ainda temos alguns problemas

Ainda há outra propriedade importante que não estamos testando.

A `Contagem` deve ter uma pausa para cada impressão, como por exemplo:

* `Pausa`
* `Imprime N`
* `Pausa`
* `Imprime N-1`
* `Pausa`
* `Imprime Vai!`
* etc

Nossa alteração mais recente só verifica se o software teve 4 pausas, mas essas pausas poderiam ocorrer fora de ordem.

Quando escrevemos testes, se não estiver confiante de que seus testes estão te dando confiança o suficiente, quebre-o (mas certifique-se de que você salvou suas alterações antes)! Mude o código para o seguinte:

```go
func Contagem(saida io.Writer, sleeper Sleeper) {
    for i := inicioContagem; i > 0; i-- {
        sleeper.Pausa()
        fmt.Fprintln(saida, i)
    }

    for i := inicioContagem; i > 0; i-- {
        fmt.Fprintln(saida, i)
    }

    sleeper.Pausa()
    fmt.Fprint(saida, ultimaPalavra)
}
```

Se executar seus testes, eles ainda vão passar, apesar da implementação estar errada.

Vamos usar o spy novamente com um novo teste para verificar se a ordem das operações está correta.

Temos duas dependências diferentes e queremos gravar todas as operações delas em uma lista. Logo, vamos criar *um spy para ambas*.

```go
type SpyContagemOperacoes struct {
    Chamadas []string
}

func (s *SpyContagemOperacoes) Pausa() {
    s.Chamadas = append(s.Chamadas, pausa)
}

func (s *SpyContagemOperacoes) Write(p []byte) (n int, err error) {
    s.Chamadas = append(s.Chamadas, escrita)
    return
}

const escrita = "escrita"
const pausa = "pausa"
```

Nosso `SpyContagemOperacoes` implementa tanto o `io.Writer` quanto o `Sleeper`, gravando cada chamada em um slice. Nesse teste, temos preocupação apenas na ordem das operações, então apenas gravá-las em uma lista de operações nomeadas é suficiente.

Agora podemos adicionar um subteste no nosso conjunto de testes.

```go
t.Run("pausa antes de cada impressão", func(t *testing.T) {
        spyImpressoraSleep := &SpyContagemOperacoes{}
        Contagem(spyImpressoraSleep, spyImpressoraSleep)

        esperado := []string{
            pausa,
            escrita,
            pausa,
            escrita,
            pausa,
            escrita,
            pausa,
            escrita,
        }

        if !reflect.DeepEqual(esperado, spyImpressoraSleep.Chamadas) {
            t.Errorf("esperado %v chamadas, resultado %v", esperado, spyImpressoraSleep.Chamadas)
        }
    })
```

Esse teste deve falhar. Volte o código que quebramos para a versão correta e agora o novo teste deve passar.

Agora temos dois spies no `Sleeper`. O próximo passo é refatorar nosso teste para que um teste o que está sendo impresso e o outro se certifique de que estamos pausando entre as impressões. Por fim, podemos apagar nosso primeiro spy, já que não é mais utilizado.

```go
func TestContagem(t *testing.T) {

    t.Run("imprime 3 até Vai!", func(t *testing.T) {
        buffer := &bytes.Buffer{}
        Contagem(buffer, &SpyContagemOperacoes{})

        resultado := buffer.String()
        esperado := `3
2
1
Vai!`

        if resultado != esperado {
            t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
        }
    })

    t.Run("pausa antes de cada impressão", func(t *testing.T) {
        spyImpressoraSleep := &SpyContagemOperacoes{}
        Contagem(spyImpressoraSleep, spyImpressoraSleep)

        esperado := []string{
            pausa,
            escrita,
            pausa,
            escrita,
            pausa,
            escrita,
            pausa,
            escrita,
        }

        if !reflect.DeepEqual(esperado, spyImpressoraSleep.Chamadas) {
            t.Errorf("esperado %v chamadas, resultado %v", esperado, spyImpressoraSleep.Chamadas)
        }
    })
}
```

Agora temos nossa função e suas duas propriedades testadas adequadamente.

## Extendendo o Sleeper para se tornar configurável

Uma funcionalidade legal seria o `Sleeper` ser configurável.

### Escreva o teste primeiro

Agora vamos criar um novo tipo para `SleeperConfiguravel` que aceita o que precisamos para configuração e teste.

```go
type SleeperConfiguravel struct {
	duracao time.Duration
	pausa   func(time.Duration)
}
```

Estamos usando a `duracao` para configurar o tempo de pausa e `pausa` como forma de passar uma função de pausa. A assinatura de `sleep` é a mesma de `time.Sleep`, nos permitindo usar `time.Sleep` na nossa implementação real e um spy nos nossos testes.

```go
type TempoSpy struct {
	duracaoPausa time.Duration
}

func (t *TempoSpy) Pausa(duracao time.Duration) {
	t.duracaoPausa = duracao
}
```

Definindo nosso spy, podemos criar um novo teste para o sleeper configurável.

```go
func TestSleeperConfiguravel(t *testing.T) {
    tempoPausa := 5 * time.Second

    tempoSpy := &TempoSpy{}
    sleeper := SleeperConfiguravel{tempoPausa, tempoSpy.Pausa}
    sleeper.Pausa()

    if tempoSpy.duracaoPausa != tempoPausa {
        t.Errorf("deveria ter pausado por %v, mas pausou por %v", tempoPausa, tempoSpy.duracaoPausa)
    }
}
```

Não há nada de novo nesse teste e seu funcionamento é bem semelhante aos testes com mock anteriores.

### Execute o teste

```bash
sleeper.Pausa undefined (type SleeperConfiguravel has no field or method Pausa, but does have pausa)
```

`sleeper.Pausa não definido (tipo SleeperConfiguravel não tem campo ou método Pausa, mas tem o método sleep`

Você deve ver uma mensagem de erro bem clara indicando que não temos um método `Pausa` criado no nosso `SleeperConfiguravel`.

### Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (c *SleeperConfiguravel) Pausa() {
}
```

Com nossa nova função `Pausa` implementada, ainda há um teste falhando.

```bash
contagem_test.go:56: deveria ter pausado por 5s, mas pausou por 0s
```

### Escreva código o suficiente para fazer o teste passar

Tudo o que precisamos fazer agora é implementar a função `Pausa` para o `SleeperConfiguravel`.

```go
func (s *SleeperConfiguravel) Pausa() {
    s.pausa(s.duracao)
}
```

Com essa mudança, todos os testes devem voltar a passar.

### Limpeza e refatoração

A última coisa que precisamos fazer é de fato usar nosso `SleeperConfiguravel` na função main.

```go
func main() {
    sleeper := &SleeperConfiguravel{1 * time.Second, time.Sleep}
    Contagem(os.Stdout, sleeper)
}
```

Se executarmos os testes e o programa manualmente, podemos ver que todo o comportamento permanece o mesmo.

Já que estamos usando o `SleeperConfiguravel`, é seguro deletar o `SleeperPadrao`.

## Mas o mock não é do demonho?

Você já deve ter ouvido que o mock é do mal. Quase qualquer coisa no desenvolvimento de software pode ser usada para o mal, assim como o [DRY](https://pt.wikipedia.org/wiki/Don%27t_repeat_yourself).

As pessoas acabam chegando numa fase ruim em que não *dão atenção aos próprios testes* e *não respeitam a etapa de refatoração*.

Se seu código de mock estiver ficando complicado ou você tem que mockar muita coisa para testar algo, você deve *prestar mais atenção* a essa sensação ruim e pensar sobre o seu código. Geralmente isso é sinal de que:

* A coisa que você está testando está tendo que fazer coisas demais
  * Modularize a função para que faça menos coisas
* Suas dependências estão muito desacopladas
  * Pense e uma forma de consolidar algumas das dependências em um módulo útil
* Você está se preocupando demais com detalhes de implementação
  * Dê prioridade em testar o comportamento esperado ao invés da implementação

Normalmente, muitos pontos de mock são sinais de *abstração ruim* no seu código.

**As pessoas costumam pensar que essa é uma fraqueza no TDD, mas na verdade é um ponto forte**. Testes mal desenvolvidos são resultado de código ruim. Código bem desenvolvido é fácil de ser testado.

### Só que mocks e testes ainda estão dificultando minha vida!

Já se deparou com a situação a seguir?

* Você quer refatorar algo
* Para isso, você precisa mudar vários testes
* Você duvida do TDD e cria um post no Medium chamado "Mock é prejudicial"

Isso costuma ser um sinal de que você está testando muito *detalhe de implementação*. Tente fazer de forma que esteja testando *comportamentos úteis*, a não ser que a implementação seja tão importante que a falta dela possa fazer o sistema quebrar.

Às vezes é difícil saber *qual nível* testar exatamente, então aqui vai algumas ideias e regras que tento seguir:

-**A definição de refatoração é que o código muda, mas o comportamento permanece o mesmo**. Se você decidiu refatorar alguma coisa, na teoria você deve ser capaz de salvar seu código sem que o teste mude. Então, quando estiver escrevendo um teste, pergunte para si: - Estou testando o comportamento que quero ou detalhes de implementação? - Se fosse refatorar esse código, eu teria que fazer muitas mudanças no meu teste?

* Apesar do Go te deixar testar funções privadas, eu evitaria fazer isso, já que funções privadas costumam ser detalhes de implementação.
* Se o teste estiver com **3 mocks, esse é um sinal de alerta** - hora de repensar no design.
* Use spies com cuidado. Spies te deixam ver a parte interna do algoritmo que você está escrevendo, o que pode ser bem útil, mas significa que há um acoplamento maior entre o código do teste e a implementação. **Certifique-se de que você realmente precisa desses detalhes se você vai colocar um spy neles**.

Como sempre, regras no desenvolvimento de software não são realmente regras e podem haver exceções. [O artigo do Uncle Bob sobre "Quando mockar"](https://8thlight.com/blog/uncle-bob/2014/05/10/WhenToMock.html) (em inglês) tem alguns pontos excelentes.

## Resumo

### Mais sobre abordagem TDD

* Quando se deparar com exemplos menos comuns, divida o problema em "linhas verticais finas". Tente chegar em um ponto onde você tem *software em funcionamento com o apoio de testes* o mais rápido possível, para evitar cair em armadilhas e se perder.
* Quando tiver uma parte do software em funcionamento, deve ser mais fácil *iterar com etapas pequenas* até chegar no software que você precisa.

> "Quando usar o desenvolvimento iterativo? Apenas em projetos que você quer obter sucesso."

Martin Fowler.

### Mock

* **Sem o mock, partes importantes do seu código não serão testadas**. No nosso caso, não seríamos capazes de testar se nosso código pausava em cada impressão, mas existem inúmeros exemplos. Chamar um serviço que *pode* falhar? Querer testar seu sistema em um estado em particular? É bem difícil testar esses casos sem mock.
* Sem mocks você pode ter que definir bancos de dados e outras dependências externas só para testar regras de negócio simples. Seus testes provavelmente ficarão mais lentos, resultando em **loops de feedback lentos**.
* Ter que se conectar a um banco de dados ou webservice para testar algo vai tornar seus testes **frágeis** por causa da falta de segurança nesses serviços.

Uma vez que a pessoa aprende a mockar, é bem fácil testar pontos demais de um sistema em termos da *forma que ele funciona* ao invés *do que ele faz*. Sempre tenha em mente o **valor dos seus testes** e qual impacto eles teriam em uma refatoração futura.

Nesse artigo sobre mock, falamos sobre **spies**, que são um tipo de mock. Aqui estão diferentes tipos de mocks. [O Uncle Bob explica os tipos em um artigo bem fácil de ler](https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html) (em inglês). Nos próximos capítulos, vamos precisar escrever código que depende de outros para obter dados, que é aonde vou mostrar os **Stubs** em ação.


# Concorrência

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/concorrencia)

A questão é a seguinte: um colega escreveu uma função, `VerificaWebsites`, que verifica o status de uma lista de URLs.

```go
package concorrencia

type VerificadorWebsite func(string) bool

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)

    for _, url := range urls {
        resultados[url] = vw(url)
    }

    return resultados
}
```

Ela retorna um map de cada URL verificado com um valor booleano - `true` para uma boa resposta, `false` para uma resposta ruim.

Você também tem que passar um `VerificadorWebsite` como parâmetro, que leva um URL e retorna um boleano. Isso é usado pela função que verifica todos os websites.

Usando a [injeção de dependência](/aprenda-go-com-testes/main/primeiros-passos-com-go/injecao-de-dependencia), conseguimos testar a função sem fazer chamadas HTTP de verdade, tornando o teste seguro e rápido.

Aqui está o teste que escreveram:

```go
package concurrency

import (
    "reflect"
    "testing"
)

func mockVerificadorWebsite(url string) bool {
    if url == "waat://furhurterwe.geds" {
        return false
    }
    return true
}

func TestVerificaWebsites(t *testing.T) {
    websites := []string{
        "http://google.com",
        "http://blog.gypsydave5.com",
        "waat://furhurterwe.geds",
    }

    esperado := map[string]bool{
        "http://google.com":          true,
        "http://blog.gypsydave5.com": true,
        "waat://furhurterwe.geds":    false,
    }

    resultado := VerificaWebsites(mockVerificadorWebsite, websites)

    if !reflect.DeepEqual(esperado, resultado) {
        t.Fatalf("esperado %v, resultado %v", esperado, resultado)
    }
}
```

A função que está em produção está sendo usada para verificar centenas de websites. Só que seu colega começou a reclamar que está lento demais, e pediram sua ajuda para melhorar a velocidade dele.

## Escreva o teste primeiro

Vamos usar um teste de benchmark para testar a velocidade de `VerificaWebsites` para que possamos ver o efeito das nossas alterações.

```go
package concorrencia

import (
    "testing"
    "time"
)

func slowStubVerificadorWebsite(_ string) bool {
    time.Sleep(20 * time.Millisecond)
    return true
}

func BenchmarkVerificaWebsites(b *testing.B) {
    urls := make([]string, 100)
    for i := 0; i < len(urls); i++ {
        urls[i] = "uma url"
    }

    for i := 0; i < b.N; i++ {
        VerificaWebsites(slowStubVerificadorWebsite, urls)
    }
}
```

O benchmark testa `VerificaWebsites` usando um slice de 100 URLs e usa uma nova implementação falsa de `VerificadorWebsite`. `slowStubVerificadorWebsite` é intencionalmente lento. Ele usa um `time.Sleep` para esperar exatamente 20 milissegundos e então retorna verdadeiro.

Quando executamos o benchmark com `go test -bench=.` (ou, se estiver no Powershell do Windows, `go test -bench="."`):

```bash
pkg: github.com/larien/aprenda-go-com-testes/concorrencia/v1
BenchmarkVerificaWebsites-4               1        2249228637 ns/op
PASS
ok      github.com/larien/aprenda-go-com-testes/concorrencia/v1        2.268s
```

`VerificaWebsites` teve uma marca de 2249228637 nanosegundos - pouco mais de dois segundos.

Vamos torná-lo mais rápido.

### Escreva código o suficiente para fazer o teste passar

Agora finalmente podemos falar sobre concorrência que, apenas para fins dessa situação, significa "fazer mais do que uma coisa ao mesmo tempo". Isso é algo que fazemos naturalmente todo dia.

Por exemplo, hoje de manhã fiz uma xícara de chá. Coloquei a chaleira no fogo e, enquanto esperava a água ferver, tirei o leite da geladeira, tirei o chá do armário, encontrei minha xícara favorita, coloquei o saquinho do chá e, quando a chaleira ferveu a água, coloquei a água na xícara.

O que eu *não fiz* foi colocar a chaleira no fogo e então ficar sem fazer nada só esperando a chaleira ferver a água, para depois fazer todo o restante quando a água tivesse fervido.

Se conseguir entender por que é mais rápido fazer chá da primeira forma, então você é capaz de entender como vamos tornar o `VerificaWebsites` mais rápido. Ao invés de esperar por um website responder antes de enviar uma requisição para o próximo website, vamos dizer para nosso computador fazer a próxima requisição enquanto espera pela primeira.

Normalmente, em Go, quando chamamos uma função `fazAlgumaCoisa()`, esperamos que ela retorne alguma coisa (mesmo se não tiver valor para retornar, ainda esperamos que ela termine). Chamamos essa operação de *bloqueante* - espera algo acabar para terminar seu trabalho. Uma operação que não bloqueia no Go vai rodar em um *processo* separado, chamado de *goroutine*. Pense no processo como uma leitura de uma página de código Go de cima para baixo, 'entrando' em cada função quando é chamado para ler o que essa página faz. Quando um processo separado começa, é como se outro leitor começasse a ler o interior da função, deixando o leitor original continuar lendo a página.

Para dizer ao Go começar uma nova goroutine, transformamos a chamada de função em uma declaração `go` colocando a palavra-chave `go` na frente da função: `go fazAlgumaCoisa()`.

```go
package concurrency

type VerificadorWebsite func(string) bool

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)

    for _, url := range urls {
        go func() {
            resultados[url] = vw(url)
        }()
    }

    return resultados
}
```

Já que a única forma de começar uma goroutine é colocar `go` na frente da chamada de função, costumamos usar *funções anônimas* quando queremos iniciar uma goroutine. Uma função anônima literal é bem parecida com uma declaração de função normal, mas (obviamente) sem um nome. Você pode ver uma acima no corpo do laço `for`.

Funções anônimas têm várias funcionalidades que as tornam úteis, duas das quais estamos usando acima. Primeiramente, elas podem ser executadas assim que fazemos sua declaração - que é o `()` no final da função anônima. Em segundo lugar, elas mantém acesso ao escopo léxico em que são definidas - todas as variáveis que estão disponíveis no ponto em que a função anônima é declarada também estão disponíveis no corpo da função.

O corpo da função anônima acima é quase o mesmo da função no laço utilizada anteriormente. A única diferença é que cada iteração do loop vai iniciar uma nova goroutine, concorrente com o processo atual (a função `VerificadorWebsite`), e cada uma vai adicionar seu resultado ao map de resultados.

```bash
--- FAIL: TestVerificaWebsites (0.00s)
        VerificaWebsites_test.go:31: esperado map[http://google.com:true http://blog.gypsydave5.com:true waat://furhurterwe.geds:false], resultado map[]
FAIL
exit status 1
FAIL    github.com/larien/aprenda-go-com-testes/concorrencia/v2        0.010s
```

### Uma breve visita ao universo paralelo...

Você pode não ter obtido esse resultado. Você pode obter uma mensagem de pânico, que vamos falar sobre em breve. Não se preocupe se isso aparecer para você, basta você executar o teste até você *de fato* receber o resultado acima. Ou faça de conta que você recebeu. Escolha sua. Boas vindas à concorrência: quando não for trabalhada da forma correta, é difícil prever o que vai acontecer. Não se preocupe, é por isso que estamos escrevendo testes: para nos ajudar a saber quando estamos trabalhando com concorrência de forma previsível.

### ... e estamos de volta.

Acabou que os testes originais do `VerificadorWebsite` agora estão devolvendo um map vazio. O que deu de errado?

Nenhuma das goroutines que nosso loop `for` iniciou teve tempo de adicionar seu resultado ao map `resultados`; a função `VerificadorWebsite` é rápida demais para eles, e por isso retorna o map vazio.

Para consertar isso, podemos apenas esperar enquanto todas as goroutines fazem seu trabalho, para depois retornar. Dois segundos devem servir, certo?

```go
package concurrency

import "time"

type VerificadorWebsite func(string) bool

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)

    for _, url := range urls {
        go func() {
            resultados[url] = vw(url)
        }()
    }

    time.Sleep(2 * time.Second)

    return resultados
}
```

Agora, quando os testes forem executados, você vai ver (ou não - leia a mensagem no início do tópico):

```bash
--- FAIL: TestVerificaWebsites (0.00s)
        VerificaWebsites_test.go:31: esperado map[http://google.com:true http://blog.gypsydave5.com:true waat://furhurterwe.geds:false], resultado map[waat://furhurterwe.geds:false]
FAIL
exit status 1
FAIL    github.com/larien/aprenda-go-com-testes/concorrencia/v1        0.010s
```

Isso não é muito bom - por que só um resultado? Podemos arrumar isso aumentando o tempo de espera - pode tentar se preferir. Não vai funcionar. O problema aqui é que a variável `url` é reutilizada para cada iteração do laço `for` - ele recebe um valor novo de `urls` a cada vez. Mas cada uma das goroutines tem uma referência para a variável `url` - eles não têm sua própria cópia independente. Logo, *todas* estão escrevendo o valor que `url` tem no final da iteração - o último URL. E é por isso que o resultado que obtemos é a última URL.

Para consertar isso:

```go
package concorrencia

import (
    "time"
)

type VerificadorWebsite func(string) bool

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)

    for _, url := range urls {
        go func(u string) {
            resultados[u] = vw(u)
        }(url)
    }

    time.Sleep(2 * time.Second)

    return resultados
}
```

Ao passar cada função anônima como parâmetro para a URL - como `u` - e chamar a função anônima com `url` como argumento, nos certificamos de que o valor de `u` está fixado como o valor de `url` para cada iteração do laço de `url` e não pode ser modificado.

Agora, se você tiver sorte, vai obter:

```bash
PASS
ok      github.com/larien/aprenda-go-com-testes/concorrencia/v1        2.012s
```

No entanto, se não tiver sorte (isso é mais provável se estiver rodando o código com o benchmark, já que haverá mais tentativas):

```bash
fatal error: concurrent map writes

goroutine 37 [running]:
runtime.throw(0x6d74f3, 0x15)
    /usr/local/go/src/runtime/panic.go:608 +0x72 fp=0xc000034718 sp=0xc0000346e8 pc=0x42d4e2
runtime.mapassign_faststr(0x67dbe0, 0xc000082660, 0x6d33cb, 0x7, 0x0)
    /usr/local/go/src/runtime/map_faststr.go:275 +0x3bf fp=0xc000034780 sp=0xc000034718 pc=0x4139ff
github.com/larien/aprenda-go-com-testes/concorrencia/v2.VerificaWebsites.func1(0x6e6580, 0xc000082660, 0x6d33cb, 0x7)
    /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:17 +0x7f fp=0xc0000347c0 sp=0xc000034780 pc=0x64035f
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1333 +0x1 fp=0xc0000347c8 sp=0xc0000347c0 pc=0x45c661
created by github.com/larien/aprenda-go-com-testes/concorrencia/v2.VerificaWebsites
	/home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:16 +0xa9

        ... e mais um monte de linhas assustadoras ...
```

Isso pode ser enorme e assustador, mas tudo o que precisamos fazer é respirar com calma e ler o stacktrace: `fatal error: concurrent map writes` (erro fatal: escrita concorrente no map). Às vezes, quando executamos nossos testes, duas das goroutines escrevem no map `resultados` ao mesmo tempo. Maps em Go não gostam quando mais de uma coisa tenta escrever algo neles ao mesmo tempo, então o `erro fatal` é gerado.

Essa é uma *condição de corrida*, um bug que aparece quando a saída do nosso software depende do timing e da sequência de eventos que não temos controle sobre. Por não termos controle exato sobre quando cada goroutine escreve no map `resultados`, ficamos vulneráveis à situação de duas goroutines escreverem nele ao mesmo tempo.

O Go nos ajuda a encontrar condições de corrida com seu [*detector de corrida*](https://blog.golang.org/race-detector) nativo. Para habilitar essa funcionalidade, execute os testes com a flag `race`: `go test -race`.

Você deve ver uma saída parecida com essa:

```bash
==================
WARNING: DATA RACE
Write at 0x00c000120089 by goroutine 6:
  reflect.typedmemmove()
      /usr/local/go/src/runtime/mbarrier.go:177 +0x0
  reflect.Value.MapIndex()
      /usr/local/go/src/reflect/value.go:1124 +0x2ae
  reflect.deepValueEqual()
      /usr/local/go/src/reflect/deepequal.go:118 +0x13be
  reflect.DeepEqual()
      /usr/local/go/src/reflect/deepequal.go:196 +0x2f0
  github.com/larien/aprenda-go-com-testes/concorrencia/v2.TestVerificaWebsites()
      /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites_test.go:30 +0x1ad
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162

Previous write at 0x00c000120089 by goroutine 8:
  github.com/larien/aprenda-go-com-testes/concorrencia/v2.VerificaWebsites.func1()
      /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:17 +0x97

Goroutine 6 (running) created at:
  testing.(*T).Run()
      /usr/local/go/src/testing/testing.go:878 +0x659
  testing.runTests.func1()
      /usr/local/go/src/testing/testing.go:1119 +0xa8
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162
  testing.runTests()
      /usr/local/go/src/testing/testing.go:1117 +0x4ee
  testing.(*M).Run()
      /usr/local/go/src/testing/testing.go:1034 +0x2ee
  main.main()
      _testmain.go:44 +0x221

Goroutine 8 (finished) created at:
  github.com/larien/aprenda-go-com-testes/concorrencia/v2.VerificaWebsites()
      /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:16 +0xb2
  github.com/larien/aprenda-go-com-testes/concorrencia/v2.TestVerificaWebsites()
      /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites_test.go:28 +0x17f
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162
==================
```

Os detalhes ainda assim são bem difíceis de serem lidos - mas o `WARNING: DATA RACE` (CUIDADO: CONDIÇÃO DE CORRIDA) é bem claro. Lendo o corpo do erro podemos ver duas goroutines diferentes performando escritas em um map:

`Write at 0x00c000120089 by goroutine 6:`

está escrevendo no mesmo bloco de memória que:

`Previous write at 0x00c000120089 by goroutine 8:`

Além disso, conseguimos ver a linha de código onde a escrita está acontecendo:

`/home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:17 +0x97`

e a linha de código onde as goroutines 6 e 7 foram iniciadas:

`/home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:16 +0xb2`

Tudo o que você precisa saber está impresso no seu terminal - tudo o que você tem que fazer é ser paciente o bastante para lê-lo.

### Canais

Podemos resolver essa condição de corrida coordenando nossas goroutines usando *canais*. Canais são uma estrutura de dados em Go que pode receber e enviar valores. Essas operações, junto de seus detalhes, permitem a comunicação entre processos diferentes.

Nesse caso, queremos pensar sobre a comunicação entre o processo pai e cada uma das goroutines criadas por ele de forma que façam o trabalho de executar a função `VerificadorWebsite` com a URL.

```go
package concurrency

type VerificadorWebsite func(string) bool
type resultado struct {
    string
    bool
}

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)
    canalResultado := make(chan resultado)

    for _, url := range urls {
        go func(u string) {
            canalResultado <- resultado{u, vw(u)}
        }(url)
    }

    for i := 0; i < len(urls); i++ {
        resultado := <-canalResultado
        resultados[resultado.string] = resultado.bool
    }

    return resultados
}
```

Junto do map `resultados`, agora temos um `canalResultados`, que criamos da mesma forma usando `make`. O `chan resultado` é o tipo do canal - um canal de `resultado`. O tipo novo, `resultado`, foi criado para associar o retorno de `VerificadorWebsite` com a URL sendo verificada - é uma estrutura que contém uma `string` e um `bool`. Já que não precisamos que nenhum valor tenha um nome, cada um deles é anônimo dentro da struct; isso pode ser útil quando for difícil saber que nome dar a um valor.

Agora que iteramos pelas URLs, ao invés de escrever no `map` diretamente, enviamos uma struct `resultado` para cada chamada de `vw` para o `canalResultado` com uma *sintaxe de envio*. Essa sintaxe usa o operador `<-`, usando um canal à esquerda e um valor à direita:

```go
// Sintaxe de envio
canalResultado <- resultado{u, vw(u)}
```

O próximo laço `for` itera uma vez sobre cada uma das URLs. Dentro, estamos usando uma *expressão de recebimento*, que atribui um valor recebido por um canal a uma variável. Essa expressão também usa o operador `<-`, mas com os dois operandos ao posições invertidas: o canal agora fica à direita e a variável que está recebendo o valor dele fica à esquerda:

```go
// Expressão recebida
resultado := <-canalResultado
```

E depois usamos o `resultado` recebido para atualizar o map.

Ao enviar os resultados para um canal, podemos controlar o timing de cada escrita dentro do map `resultados`, garantindo que só aconteça uma por vez. Apesar de cada uma das chamadas de `vw` e cada envio ao canal resultado estar acontecendo em paralelo dentro de seu próprio processo, cada resultado está sendo resolvido de cada vez enquanto tiramos o valor do canal resultado com a expressão recebida.

Paralelizamos um pedaço do código que queríamos tornar mais rápida, enquanto mantivemos a parte que não pode acontecer em paralelo ainda acontecendo linearmente. E comunicamos diversos processos envolvidos utilizando canais.

Agora podemos executar o benchmark:

```bash
pkg: github.com/larien/aprenda-go-com-testes/concorrencia/v3
BenchmarkVerificaWebsites-8             100          23406615 ns/op
PASS
ok      github.com/larien/aprenda-go-com-testes/concorrencia/v3        2.377s
```

23406615 nanossegundos - 0.023 segundos, cerca de 100 vezes mais rápida que a função original. Um sucesso enorme.

## Resumo

Esse exercício foi um pouco mais leve na parte do TDD que o restante. Levamos um bom tempo refatorando a função `VerificaWebsites`; as entradas e saídas não mudaram, ela apenas ficou mais rápida. Mas, com os testes que já tinhamos escrito, assim como com o benchmark que escrevemos, fomos capazes de refatorar o `VerificaWebsites` de forma que mantivéssemos a confiança de que o software ainda estava funcionando, enquanto demonstramos que ela realmente havia ficado mais rápida.

Tornando as coisas mais rápidas, aprendemos sobre:

* *goroutines*, a unidade básica de concorrência em Go, que nos permite verificar mais do que um site ao mesmo tempo.
* *funções anônimas*, que usamos para iniciar cada um dos processos concorrentes que verificam os sites.
* *canais*, para nos ajudar a organizar e controlar a comunicação entre diferentes processos, nos permitindo evitar um bug de *condição de corrida*.
* *o detector de corrida*, que nos ajudou a desvendar problemas com código concorrente.

### Torne-o rápido

Uma formulação da forma ágil de desenvolver software, erroneamente atribuida a Kent Beck, é:

> [Faça funcionar, faça da forma certa, torne-o rápido](http://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast) (em inglês)

Onde 'funcionar' é fazer os testes passarem, 'forma certa' é refatorar o código e 'tornar rápido' é otimizar o código para, por exemplo, tornar sua execução rápida. Só podemos 'torná-lo rápido' quando fizermos funcionar da forma certa. Tivemos sorte que o código que estudamos já estava funcionando e não precisava ser refatorado. Nunca devemos tentar 'torná-lo rápido' antes das outras duas etapas terem sido feitas, porque:

> [Otimização prematura é a raiz de todo o mal](http://wiki.c2.com/?PrematureOptimization) -- Donald Knuth


# Select

[**Você pode encontrar todos os códigos desse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/select)

Te pediram para fazer uma função chamada `Corredor` que recebe duas URLs que "competirão" entre si através de uma chamada HTTP GET onde a primeira URL a responder será retornada. Se nenhuma delas responder dentro de 10 segundos a função deve retornar um `erro`.

Para isso, vamos utilizar:

* `net/http` para chamadas HTTP.
* `net/http/httptest` para nos ajudar a testar.
* goroutines.
* `select` para sincronizar processos.

## Escreva o teste primeiro

Vamos começar com algo simples.

```go
func TestCorredor(t *testing.T) {
    URLLenta := "http://www.facebook.com"
    URLRapida := "http://www.quii.co.uk"

    esperado := URLRapida
    resultado := Corredor(URLLenta, urlRapida)

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

Sabemos que não está perfeito e que existem problemas, mas é um bom início. É importante não perder tanto tempo deixando as coisas perfeitas de primeira.

## Execute o teste

`./corredor_test.go:14:9: undefined: Corredor`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func Corredor(a, b string) (vencedor string) {
    return
}
```

`corredor_test.go:25: resultado '', esperado 'http://www.quii.co.uk'`

## Escreva código suficiente para que o teste passe

```go
func Corredor(a, b string) (vencedor string) {
    inicioA := time.Now()
    http.Get(a)
    duracaoA := time.Since(inicioA)

    inicioB := time.Now()
    http.Get(b)
    duracaoB := time.Since(inicioB)

    if duracaoA < duracaoB {
        return a
    }

    return b
}
```

Para cada URL:

1. Usamos `time.Now()` para marcar o tempo antes de tentarmos pegar a `URL`.
2. Então usamos [`http.Get`](https://golang.org/pkg/net/http/#Client.Get) para tentar capturar os conteúdos da `URL`. Essa função retorna [`http.Response`](https://golang.org/pkg/net/http/#Response) e um `erro`, mas não temos interesse nesses valores.
3. `time.Since` pega o tempo inicial e retorna a diferença na forma de `time.Duration`.

Feito isso, podemos simplesmente comparar as durações e ver qual é mais rápida.

### Problemas

Isso pode ou não fazer com que o teste passe para você. O problema é que estamos acessando sites reais para testar nossa lógica.

Testar códigos que usam HTTP é tão comum que Go tem ferramentas na biblioteca padrão para te ajudar a testá-los.

Nos capítulos de [mock](/aprenda-go-com-testes/main/primeiros-passos-com-go/mocks) e [injeção de dependências](/aprenda-go-com-testes/main/primeiros-passos-com-go/injecao-de-dependencia), falamos sobre como idealmente não queremos depender de serviços externos para testar nosso código, pois:

* Podem ser lentos
* Podem ser inconsistentes
* Não conseguimos testar casos extremos

Na biblioteca padrão, existe um pacote chamado [`net/http/httptest`](https://golang.org/pkg/net/http/httptest/) onde é possível simular um servidor HTTP facilmente.

Vamos alterar nosso teste para usar essas simulações para termos servidores confiáveis para testar sob nosso controle.

```go
func TestCorredor(t *testing.T) {

    servidorLento := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(20 * time.Millisecond)
        w.WriteHeader(http.StatusOK)
    }))

    servidorRapido := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    }))

    URLLenta := servidorLento.URL
    URLRapida := servidorRapido.URL

    esperado := URLRapida
    resultado := Corredor(URLLenta, URLRapida)

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }

    servidorLento.Close()
    servidorRapido.Close()
}
```

A sintaxe pode parecer um pouco complicada, mas não tenha pressa.

`httptest.NewServer` recebe um `http.HandlerFunc` que vamos enviar para uma função *anônima*.

`http.HandlerFunc` é um tipo que se parece com isso: `type HandlerFunc func(ResponseWriter, *Requisicao)`.

Tudo o que assinatura diz é que ela precisa de uma função que recebe um `ResponseWriter` e uma `Requisição`, o que não é novidade para um servidor HTTP.

Acontece que não existe nenhuma mágica aqui, **também é assim que você escreveria um servidor HTTP** **real** **em Go**. A única diferença é que estamos utilizando ele dentro de um `httptest.NewServer` ,o que facilita seu uso em testes por ele encontrar uma porta aberta para escutar e você poder fechá-la quando estiverem concluídos dentro dos próprios testes.

Dentro de nossos dois servidores, fazemos com que um deles tenha um `time.Sleep` quando receber a requisição para torná-lo propositalmente mais lento que o outro. Ambos os servidores, então, devolvem uma resposta `OK` com `w.WriteHeader(http.StatusOK)` a quem realizou a chamada.

Se você rodar o teste novamente, ele definitivamente irá passar e deve ser mais rápido. Brinque com os **sleeps** para quebrar o teste propositalmente.

## Refatoração

Temos algumas duplicações tanto em nosso código de produção quanto em nosso código de teste.

```go
func Corredor(a, b string) (vencedor string) {
	duracaoA := medirTempoDeResposta(a)
	duracaoB := medirTempoDeResposta(b)

	if duracaoA < duracaoB {
		return a
	}

	return b
}

func medirTempoDeResposta(URL string) time.Duration {
	inicio := time.Now()
	http.Get(URL)
	return time.Since(inicio)
}
```

Essa "enxugada" torna nosso código `Corredor` bem mais legível.

```go
func TestCorredor(t *testing.T) {

	servidorLento := criarServidorComAtraso(20 * time.Millisecond)
	servidorRapido := criarServidorComAtraso(0 * time.Millisecond)

	defer servidorLento.Close()
	defer servidorRapido.Close()

	URLLenta := servidorLento.URL
	URLRapida := servidorRapido.URL

	esperado := URLRapida
	resultado := Corredor(URLLenta, URLRapida)

	if resultado != esperado {
		t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
	}
}

func criarServidorComAtraso(atraso time.Duration) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(atraso)
		w.WriteHeader(http.StatusOK)
	}))
}
```

Fizemos a refatoração criando nossos servidores falsos numa função chamada `criarServidorComAtraso` para remover alguns códigos desnecessários do nosso teste e reduzir repetições.

### `defer`

Ao chamar uma função com o prefixo `defer`, ela será chamada *após o término da função que a contém*.

Às vezes você vai precisar liberar recursos, como fechar um arquivo ou, como no nosso caso, fechar um servidor para que esse não continue escutando a uma porta.

Utilizamos o `defer` quando queremos que a função seja executada no final de uma função, mas mantendo essa instrução próxima de onde o servidor foi criado para facilitar a vida das pessoas que forem ler o código futuramente.

Nossa refatoração é uma melhoria e uma solução razoável dados os recursos de Go que vimos até aqui, mas podemos deixar essa solução ainda mais simples.

### Sincronizando processos

* Por que estamos testando a velocidade dos sites sequencialmente quando Go é ótimo com concorrência? Devemos conseguir verificar ambos ao mesmo tempo.
* Não nos preocupamos com o *tempo exato de resposta* das requisições, apenas queremos saber qual retorna primeiro.

Para fazer isso, vamos apresentar uma nova construção chamada `select` que nos ajudará a sincronizar os processos de forma mais fácil e clara.

```go
func Corredor(a, b string) (vencedor string) {
    select {
    case <-ping(a):
        return a
    case <-ping(b):
        return b
    }
}

func ping(URL string) chan bool {
    ch := make(chan bool)
    go func() {
        http.Get(URL)
        ch <- true
    }()
    return ch
}
```

#### `ping`

Definimos a função `ping` que cria um `chan bool` e a retorna.

No nosso caso, não nos *importamos* com o tipo enviado no canal, *só queremos enviar um sinal* para dizer que terminamos, então booleanos já servem.

Dentro da mesma função, iniciamos a goroutine que enviará um sinal a esse canal uma vez que a função `http.Get(URL)` tenha sido finalizada.

#### `select`

Se você se lembrar do capítulo de [concorrência](/aprenda-go-com-testes/main/primeiros-passos-com-go/concorrencia), é possível esperar os valores serem enviados a um canal com `variavel := <-ch`. Isso é uma chamada *bloqueante*, pois está aguardando por um valor.

O que o `select` te permite fazer é aguardar *múltiplos* canais. O primeiro a enviar um valor "vence" e o código abaixo do `case` é executado.

Nós usamos `ping` em nosso `select` para configurar um canal para cada uma de nossas `URL`s. Qualquer um que enviar para esse canal primeiro vai ter seu código executado no `select`, que resultará nessa `URL` sendo retornada (que consequentemente será a vencedora).

Após essas mudanças, a intenção por trás de nosso código fica bem clara e sua implementação efetivamente mais simples.

### Limites de tempo

Nosso último requisito era retornar um erro se o `Corredor` demorar mais que 10 segundos.

## Escreva o teste primeiro

```go
t.Run("retorna um erro se o servidor não responder dentro de 10s", func(t *testing.T) {
    servidorA := criarServidorComAtraso(11 * time.Second)
    servidorB := criarServidorComAtraso(12 * time.Second)

    defer servidorA.Close()
    defer servidorB.Close()

    _, err := Corredor(servidorA.URL, servidorB.URL)

    if err == nil {
        t.Error("esperava um erro, mas não obtive um")
    }
})
```

Fizemos nossos servidores de teste demorarem mais que 10s para retornar para exercitar esse cenário e agora estamos esperando que `Corredor` retorne dois valores: a URL vencedora (que ignoramos nesse teste com `_`) e um `erro`.

## Execute o teste

`./corredor_test.go:37:10: assignment mismatch: 2 variables but 1 values`

## Escreva a menor quantidade de código para rodar o teste e verifique a saída do teste que falhou

```go
func Corredor(a, b string) (vencedor string, erro error) {
    select {
    case <-ping(a):
        return a, nil
    case <-ping(b):
        return b, nil
    }
}
```

Alteramos a assinatura de `Corredor` para retornar o vencedor e um `erro`. Retornamos `nil` para nossos casos de sucesso.

O compilador vai reclamar sobre seu *primeiro teste* esperar apenas um valor, então altere essa linha para `obteve, _ := Corredor(urlLenta, urlRapida)`. Sabendo disso devemos verificar se *não* obteremos um erro em nosso caso de sucesso.

Se executar isso agora, o teste irá falhar após 11 segundos.

```
--- FAIL: TestCorredor (12.00s)
    --- FAIL: TestCorredor/retorna_um_erro_se_o_teste_não_responder_dentro_de_10s (12.00s)
        corredor_test.go:40: esperava um erro, mas não obtive um.
```

## Escreva código o suficiente para fazer o teste passar

```go
func Corredor(a, b string) (vencedor string, erro error) {
    select {
    case <-ping(a):
        return a, nil
    case <-ping(b):
        return b, nil
    case <-time.After(10 * time.Second):
        return "", fmt.Errorf("tempo limite de espera excedido para %s e %s", a, b)
    }
}
```

`time.After` é uma função muito útil quando usamos `select`. Embora não ocorra em nosso caso, você pode escrever um código que bloqueia para sempre se os canais que o `select` estiver ouvindo nunca retornarem um valor. `time.After` retorna um `chan` (como `ping`) e te enviará um sinal após a quantidade de tempo definida.

Para nós isso é perfeito; se `a` ou `b` conseguir retornar teremos um vencedor, mas se chegar a 10 segundos nosso `time.After` nos enviará um sinal e retornaremos um `erro`.

### Testes lentos

O problema que temos é que esse teste demora 10 segundos para rodar. Para uma lógica tão simples, isso não parece ótimo.

O que podemos fazer é deixar esse esgotamento de tempo configurável. Então, em nosso teste, podemos ter um tempo bem curto e, quando utilizado no mundo real, esse tempo ser definido para 10 segundos.

```go
func Corredor(a, b string, tempoLimite time.Duration) (vencedor string, erro error) {
    select {
    case <-ping(a):
        return a, nil
    case <-ping(b):
        return b, nil
    case <-time.After(tempoLimite):
        return "", fmt.Errorf("tempo limite de espera excedido para %s e %s", a, b)
    }
}
```

Nosso teste não irá compilar pois não fornecemos um tempo de expiração.

Antes de nos apressar para adicionar esse valor padrão a ambos os testes, vamos *ouvi-los*.

* Nos importamos com o tempo excedido em nosso caso de teste de sucesso?
* Os requisitos foram explícitos sobre o tempo limite?

Dado esse conhecimento, vamos fazer uma pequena refatoração para ser simpático aos nossos testes e aos usuários de nosso código.

```go
var limiteDeDezSegundos = 10 * time.Second

func Corredor(a, b string) (vencedor string, erro error) {
    return Configuravel(a, b, limiteDeDezSegundos)
}

func Configuravel(a, b string, tempoLimite time.Duration) (vencedor string, erro error) {
    select {
    case <-ping(a):
        return a, nil
    case <-ping(b):
        return b, nil
    case <-time.After(tempoLimite):
        return "", fmt.Errorf("tempo limite de espera excedido para %s e %s", a, b)
    }
}
```

Nossos usuários e nosso primeiro teste podem utilizar `Corredor` (que usa `Configuravel` por baixo dos panos) e nosso caminho triste pode usar `Configuravel`.

```go
func TestCorredor(t *testing.T) {
	t.Run("compara a velocidade de servidores, retornando o endereço do mais rápido", func(t *testing.T) {
		servidorLento := criarServidorComAtraso(20 * time.Millisecond)
		servidorRapido := criarServidorComAtraso(0 * time.Millisecond)

		defer servidorLento.Close()
		defer servidorRapido.Close()

		URLLenta := servidorLento.URL
		URLRapida := servidorRapido.URL

		esperado := URLRapida
		resultado, err := Corredor(URLLenta, URLRapida)

		if err != nil {
			t.Fatalf("não esperava um erro, mas obteve um %v", err)
		}

		if resultado != esperado {
			t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
		}
	})

	t.Run("retorna um erro se o servidor não responder dentro de 10s", func(t *testing.T) {
		servidor := criarServidorComAtraso(25 * time.Millisecond)

		defer servidor.Close()

		_, err := Configuravel(servidor.URL, servidor.URL, 20*time.Millisecond)

		if err == nil {
			t.Error("esperava um erro, mas não obtive um")
		}
	})
}
```

Adicionei uma verificação final ao primeiro teste para saber se não pegamos um `erro`.

## Resumo

### `select`

* Ajuda você a escutar vários canais.
* Às vezes você pode precisar incluir `time.After` em um de seus `cases` para prevenir que seu sistema fique bloqueado para sempre.

### `httptest`

* Uma forma conveniente de criar servidores de teste para que se tenha testes confiáveis e controláveis.
* Usa as mesmas interfaces que servidores `net/http` reais, o que torna seu sistema consistente e gera menos coisas para você aprender.


# Reflexão

[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/reflection)

[Do Twitter](https://twitter.com/peterbourgon/status/1011403901419937792?s=09)

> Desafio Golang: escreva uma função `percorre(x interface{}, fn func(string))` que recebe uma struct `x` e chama `fn` para todos os campos string encontrados dentro dela. nível de dificuldade: recursão.

Para fazer isso vamos precisar usar `reflection` (reflexão).

> A reflexão em computação é a habilidade de um programa examinar sua própria estrutura, particularmente através de tipos; é uma forma de metaprogramação. Também é uma ótima fonte de confusão.

De [The Go Blog: Reflection](https://blog.golang.org/laws-of-reflection)

## O que é `interface`?

Aproveitamos a segurança de tipos que o Go nos ofereceu em termos de funções que funcionam com tipos conhecidos, como `string`, `int` e nossos próprios tipos como `ContaBancaria`.

Isso significa que de praxe temos documentação e o compilador vai reclamar se você tentar passar o tipo errado para uma função.

Só que você pode se deparar com situações em que quer escrever uma função, mas não sabe o tipo da variável em tempo de compilação.

Go nos permite contornar isso com o tipo `interface{}`, que você pode relacionar com *qualquer* tipo.

Logo, `percorre(x interface{}, fn func(string))` aceitará qualquer valor para `x`.

### Então por que não usar `interface` para tudo e ter funções bem flexíveis?

* Quando utiliza uma função que usa `interface`, você perde a segurança de tipos. E se você quisesse passar `Foo.bar` do tipo `string` para uma função, mas ao invés disso passa `Foo.baz` do tipo `int`? O compilador não vai ser capaz de informar seu erro. Você também não tem ideia *do que* pode passar para uma função. Saber que uma função recebe um `ServicoDeUsuario`, por exemplo, é muito útil.

Resumindo, só use reflexão quando realmente precisar.

Se quiser funções polimórficas, considere desenvolvê-la em torno de uma interface (não `interface{}`, só para esclarecer) para que os usuários possam usar sua função com vários tipos se implementarem os métodos que você precisar para a sua função funcionar.

Nossa função vai precisar ser capaz de trabalhar com várias coisas diferentes. Como sempre, vamos usar uma abordagem iterativa, escrevendo testes para cada coisa nova que quisermos dar suporte e refatorando ao longo do caminho até finalizarmos.

## Escreva o teste primeiro

Vamos chamar nossa função com uma estrutura que tem um campo string dentro (`x`). Depois, podemos espiar a função (`fn`) passada para ela para ver se ela foi chamada.

```go
func TestPercorre(t *testing.T) {

    esperado := "Chris"
    var resultado []string

    x := struct {
        Nome string
    }{esperado}

    percorre(x, func(entrada string) {
        resultado = append(resultado, entrada)
    })

    if len(resultado) != 1 {
        t.Errorf("número incorreto de chamadas de função: resultado %d, esperado %d", len(resultado), 1)
    }
}
```

* Queremos armazenar um slice de strings (`resultado`) que armazena quais strings foram passadas dentro de `fn` pelo `percorre`. Algumas vezes, nos capítulos anteriores, criamos tipos dedicados para isso para espionar chamadas de função/método, mas nesse caso vamos apenas passá-lo em uma função anônima para `fn` que acaba em `resultado`.
* Usamos uma `struct` anônima com um campo `Nome` do tipo string para partir para caminho "feliz" e mais simples.
* Finalmente, chamamos `percorre` com `x` e o espião e por enquanto só verificamos o tamanho de `resultado`. Teremos mais precisão nas nossas verificações quando tivermos algo bem básico funcionando.

## Tente executar o teste

```
./reflection_test.go:21:2: undefined: percorre
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Precisamos definir `percorre`.

```go
func percorre(x interface{}, fn func(entrada string)) {

}
```

Execute o teste novamente:

```
=== RUN   TestPercorre
--- FAIL: TestPercorre (0.00s)
    reflection_test.go:19: número incorreto de chamadas de função: resultado 0, esperado 1
FAIL
```

### Escreva código o suficiente para fazer o teste passar

Agora podemos chamar o espião com qualquer string para fazer o teste passar.

```go
func percorre(x interface{}, fn func(entrada string)) {
    fn("Ainda não acredito que o Brasil perdeu de 7 a 1")
}
```

Agora o teste deve estar passando. A próxima coisa que vamos precisar fazer é criar uma verificação mais específica do que está sendo chamado dentro do nosso `fn`.

## Escreva o teste primeiro

Adicione o código a seguir para o teste existente para verificar se a string passada para `fn` está correta:

```go
if resultado[0] != esperado {
    t.Errorf("resultado '%s', esperado '%s'", resultado[0], esperado)
}
```

## Execute o teste

```
=== RUN   TestPercorre
--- FAIL: TestPercorre (0.00s)
    reflection_test.go:23: resultado 'Ainda não acredito que o Brasil perdeu de 7 a 1', esperado 'Chris'
FAIL
```

### Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := reflect.ValueOf(x) // ValorDe
	campo := valor.Field(0)     // Campo
	fn(campo.String())
}
```

Esse código está *pouco seguro e muito frágil*, mas lembre-se que nosso objetivo quando estamos no "vermelho" (os testes estão falhando) é escrever a menor quantidade de código possível. Depois escrevemos mais testes para resolver nossas lacunas.

Precisamos usar o reflection para verificar as propriedades de `x`.

No [pacote reflect](https://godoc.org/reflect) existe uma função chamada `ValueOf` que retorna um `Value` (valor) de determinada variável. Isso nos permite inspecionar um valor, inclusive seus campos usados nas próximas linhas.

Então podemos presumir coisas bem otimistas sobre o valor passado:

* Podemos procurar pelo primeiro e único campo, mas pode não haver nenhum campo, o que causaria um pânico.
* Depois podemos chamar `String()` que tetorna o valor subjacente como string, mas sabemos que vai dar errado se o campo for de algum tipo que não uma string.

## Refatoração

Nosso código está passando pelo caso simples, mas sabemos que nosso código tem várias falhas.

Vamos escrever alguns testes onde passamos valores diferentes e verificaremos o array de strings com que `fn` foi chamado.

Precisamos refatorar nosso teste em um teste orientado por tabelas para tornar esse processo mais fácil para continuarmos testando novas situações.

```go
func TestPercorre(t *testing.T) {

	casos := []struct {
		Nome              string
		Entrada           interface{}
		ChamadasEsperadas []string
	}{
		{
			"Struct com um campo string",
			struct {
				Nome string
			}{"Chris"},
			[]string{"Chris"},
		},
	}

	for _, teste := range casos {
		t.Run(teste.Nome, func(t *testing.T) {
			var resultado []string
			percorre(teste.Entrada, func(entrada string) {
				resultado = append(resultado, entrada)
			})

			if !reflect.DeepEqual(resultado, teste.ChamadasEsperadas) {
				t.Errorf("resultado %v, esperado %v", resultado, teste.ChamadasEsperadas)
			}
		})
	}
}
```

Agora podemos adicionar uma situação facilmente para ver o que acontece se tivermos mais de um campo string.

## Escreva o teste primeiro

Adicione o cenário a seguir nos `casos`.

```go
{
	"Struct com dois campos tipo string",
	struct {
		Nome   string
		Cidade string
	}{"Chris", "Londres"},
	[]string{"Chris", "Londres"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Struct_com_dois_campos_string
    --- FAIL: TestPercorre/Struct_com_dois_campos_string (0.00s)
        reflection_test.go:40: resultado [Chris], esperado [Chris Londres]
```

## Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := reflect.ValueOf(x)

	for i := 0; i < valor.NumField(); i++ {
		campo := valor.Field(i)
		fn(campo.String())
	}
}
```

`valor` tem um método chamado `NumField` que retorna a quantidade de campos no valor. Isso nos permite iterar sobre os campos e chamar `fn`, o que faz nosso teste passar.

## Refatoração

Não parece haver nenhuma refatoração óbvia aqui que pode melhorar nosso código, então vamos continuar.

A próxima falha em `percorre` é que ela presume que todo campo é uma `string`. Vamos escrever um teste para esse caso.

## Escreva o teste primeiro

Inclua o seguinte cenário:

```go
{
	"Struct sem campo tipo string",
	struct {
		Nome  string
		Idade int
	}{"Chris", 33},
	[]string{"Chris"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Struct_sem_campo_tipo_string
    --- FAIL: TestPercorre/Struct_with_noStruct_sem_campo_tipo_stringn_string_field (0.00s)
        reflection_test.go:46: resutado [Chris <int Value>], esperado [Chris]
```

## Escreva código o suficiente para fazer o teste passar

Precisamos verificar que o tipo do campo é uma `string`.

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := reflect.ValueOf(x)

	for i := 0; i < valor.NumField(); i++ {
		campo := valor.Field(i)

		if campo.Kind() == reflect.String { // Tipo
			fn(campo.String())
		}
	}
}
```

Podemos verificar seu tipo chamando a função [`Kind`](https://godoc.org/reflect#Kind).

## Refatoração

Parece que o código ainda está razoável por enquanto.

O próximo caso é: e se o valor não for uma `struct` "única"? Em outras palavras, o que acontece se tivermos uma `struct` com alguns campos aninhados?

## Escreva o teste primeiro

Estivemos usando a sintaxe de estrutura anônima para declarar tipos conforme precisávamos para nossos testes, então poderíamos continuar a fazer isso, como:

```go
{
    "Campos aninhados",
    struct {
        Nome string
        Perfil struct {
            Idade  int
            Cidade string
        }
    }{"Chris", struct {
        Idade  int
        Cidade string
    }{33, "Londres"}},
    []string{"Chris", "Londres"},
},
```

Mas podemos ver que quando você usa estruturas anônimas cada vez mais aninhadas, a sintaxe fica um pouco bagunçada. [Há uma proposta para fazer isso de forma que a sintaxe seja mais agradável](https://github.com/golang/go/issues/12854).

Vamos apenas refatorar isso criando um tipo conhecido para esse caso e referenciá-lo no nosso teste. Não é aconselhável colocar código do teste fora do teste, mas as pessoas devem ser capazes de encontrar essas estruturas procurando por sua definição.

Inclua as seguintes declarações de tipos no seu arquivo de teste:

```go
type Pessoa struct {
	Nome   string
	Perfil Perfil
}

type Perfil struct {
	Idade  int
	Cidade string
}
```

Agora podemos adicionar isso aos nossos casos ficarem bem mais legíveis que antes:

```go
{
	"Campos aninhados",
	Pessoa{
		"Chris",
		Perfil{33, "Londres"},
	},
	[]string{"Chris", "Londres"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Campps_aninhados
    --- FAIL: TestPercorre/Campps_aninhados (0.00s)
        reflection_test.go:54: resultado [Chris], esperado [Chris Londres]
```

O problema é que estamos apenas iterando sobre os campos no primeiro nível da hierarquia de tipos.

## Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := reflect.ValueOf(x)

	for i := 0; i < valor.NumField(); i++ {
		campo := valor.Field(i)

        if campo.Kind() == reflect.String {
            fn(campo.String())
        }

        if campo.Kind() == reflect.Struct {
            percorre(campo.Interface(), fn)
        }
    }
}
```

A solução é bem simples. Inspecionamos seu tipo novamente e se for uma estrutura apenas chamamos `percorre` novamente na nossa estrutura de dentro.

## Refatoração

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := reflect.ValueOf(x)

	for i := 0; i < valor.NumField(); i++ {
		campo := valor.Field(i)

		switch campo.Kind() {
		case reflect.String:
			fn(campo.String())
		case reflect.Struct:
			percorre(campo.Interface(), fn)
		}
	}
}
```

Quando você está fazendo uma comparação de mesmo valor mais de uma vez, *geralmente* refatorar as condições dentro de um `switch` vai melhorar a legibilidade e tornar seu código mais fácil de estender.

E se o valor passado na estrutura for um ponteiro?

## Escreva o teste primeiro

Inclua esse caso:

```go
{
	"Ponteiros para coisas",
	&Pessoa{
		"Chris",
		Perfil{33, "Londres"},
	},
	[]string{"Chris", "Londres"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Ponteiros_para_coisas
panic: reflect: call of reflect.Value.NumField on ptr Value [recovered]
    panic: reflect: call of reflect.Value.NumField on ptr Value
```

## Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := reflect.ValueOf(x)

    if valor.Kind() == reflect.Ptr {
        valor = valor.Elem()
    }

    for i := 0; i < valor.NumField(); i++ {
		campo := valor.Field(i)

		switch campo.Kind() {
		case reflect.String:
			fn(campo.String())
		case reflect.Struct:
			percorre(campo.Interface(), fn)
		}
	}
}
```

Não é possível usar o `NumField` em um ponteiro `Value` e precisamos extrair o valor antes disso usando `Elem()`.

## Refatoração

Vamos encapsular a responsabilidade de extrair o `reflect.Value` de determinada `interface{}` para uma função.

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := obtemValor(x)

	for i := 0; i < valor.NumField(); i++ {
		campo := valor.Field(i)

		switch campo.Kind() {
		case reflect.String:
			fn(campo.String())
		case reflect.Struct:
			percorre(campo.Interface(), fn)
		}
	}
}

func obtemValor(x interface{}) reflect.Value {
	valor := reflect.ValueOf(x)

	if valor.Kind() == reflect.Ptr {
		valor = valor.Elem()
	}

	return valor
}
```

Isso acaba adicionando *mais* código, mas me parece que o nível de abstração está correto.

* Obter o `reflect.Value` de `x` para que eu possa inspecioná-lo, não me importa de qual forma.
* Iterar pelos campos, fazendo o que for necessário dependendo de seu tipo.

Depois precisamos lidar com os slices.

## Escreva o teste primeiro

```go
{
	"Slices",
	[]Perfil{
		{33, "Londres"},
		{34, "Reykjavík"},
	},
	[]string{"Londres", "Reykjavík"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Slices
panic: reflect: call of reflect.Value.NumField on slice Value [recovered]
    panic: reflect: call of reflect.Value.NumField on slice Value
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Esse caso se parece bastante com o do ponteiro acima, pois estamos chamar `NumField` em nosso `reflect.Value`, mas não há um por não ser uma struct.

## Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := obtemValor(x)

    if valor.Kind() == reflect.Slice {
        for i:=0; i< valor.Len(); i++ {
            percorre(valor.Index(i).Interface(), fn)
        }
        return
    }

    for i := 0; i < valor.NumField(); i++ {
        campo := valor.Field(i)

        switch campo.Kind() {
        case reflect.String:
            fn(campo.String())
        case reflect.Struct:
            percorre(campo.Interface(), fn)
        }
    }
}
```

## Refatoração

Isso funciona, mas está bagunçado. Não se preocupe, pois temos cada pedaço de código coberto por testes e podemos brincar da forma que quisermos.

Se formos pensar um pouco abstradamente, queremos chamar `percorre` em:

* Cada campo de uma estrutura
* Cada *coisa* de um slice

No momento nosso código faz isso, mas não reflete muito bem. Precisamos ter uma verificação no início da função para certificar se é um slice (com um `return` para parar a execução do restante do código) e se não for, só vamos presumir que é uma estrutura.

Vamos retrabalhar o código para verificar o tipo *primeiro* para depois fazermos o que importa.

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := obtemValor(x)

    switch valor.Kind() {
    case reflect.Struct:
        for i:=0; i<valor.NumField(); i++ {
            percorre(valor.Field(i).Interface(), fn)
        }
    case reflect.Slice:
        for i:=0; i<valor.Len(); i++ {
            percorre(valor.Index(i).Interface(), fn)
        }
    case reflect.String:
        fn(valor.String())
    }
}
```

Parece muito melhor! Se for uma estrutura ou um slice, iteramos sobre seus valores chamando `percorre` para cada um. Por outro lado, se for um `reflect.String`, podemos apenas chamar `fn`.

Ainda assim me parece que poderia ficar melhor. Há repetição da operação de iterar sobre campos/valores e chamar `percorre` sendo que conceitualmente são a mesma coisa.

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := obtemValor(x)

	quantidadeDeValores := 0
	var obtemCampo func(int) reflect.Value

	switch valor.Kind() {
	case reflect.String:
		fn(valor.String())
	case reflect.Struct:
		quantidadeDeValores = valor.NumField()
		obtemCampo = valor.Field
	case reflect.Slice:
		quantidadeDeValores = valor.Len()
		obtemCampo = valor.Index
	}

	for i := 0; i < quantidadeDeValores; i++ {
		percorre(obtemCampo(i).Interface(), fn)
	}
}
```

Se o `valor` for um `reflect.String`, chamamos `fn` normalmente.

Se for outra coisa, nosso `switch` vai extrair duas coisas dependendo do tipo:

* Quantos campos existem
* Como extrair o `Value` (`Field` \[campo] ou `Index` \[índice])

Uma vez que determinamos esses pontos, podemos iterar pela `quantidadeDeValores` chamando `percorre` com o resultado da função `getField`.

A partir disso, lidar com arrays deve ser simples.

## Escreva o teste primeiro

Inclua o caso:

```go
{
	"Arrays",
	[2]Perfil{
		{33, "Londres"},
		{34, "Reykjavík"},
	},
	[]string{"Londres", "Reykjavík"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Arrays
    --- FAIL: TestPercorre/Arrays (0.00s)
        reflection_test.go:78: resultado [], esperado [Londres Reykjavík]
```

## Escreva código o suficiente para fazer o teste passar

Podemos resolver o caso dos arrays da mesma forma que os slices, basta adicioná-los com uma vírgula:

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := obtemValor(x)

	quantidadeDeValores := 0
	var obtemCampo func(int) reflect.Value

	switch valor.Kind() {
	case reflect.String:
		fn(valor.String())
	case reflect.Struct:
		quantidadeDeValores = valor.NumField()
		obtemCampo = valor.Field
	case reflect.Slice, reflect.Array:
		quantidadeDeValores = valor.Len()
		obtemCampo = valor.Index
	}

	for i := 0; i < quantidadeDeValores; i++ {
		percorre(obtemCampo(i).Interface(), fn)
	}
}
```

O último tipo que queremos lidar é o `map`.

## Escreva o teste primeiro

```go
{
    "Maps",
    map[string]string{
        "Foo": "Bar",
        "Baz": "Boz",
    },
    []string{"Bar", "Boz"},
},
```

## Execute o teste

```
=== RUN   TestPercorre/Maps
    --- FAIL: TestPercorre/Maps (0.00s)
        reflection_test.go:86: resultado [], esperado [Bar Boz]
```

## Escreva código o suficiente para fazer o teste passar

Novamente, se pensar um pouco de forma abstrata, percebe-se que o `map` é bem parecido com a `struct`, mas as chaves são desconhecidas em tempo de compilação.

Again if you think a little abstractly you can see that `map` is very similar to `struct`, it's just the keys are unknown at compile time.

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := obtemValor(x)

    quantidadedeValores := 0
    var obtemCampo func(int) reflect.Value

    switch valor.Kind() {
    case reflect.String:
        fn(valor.String())
    case reflect.Struct:
        quantidadeDeValores = valor.NumField()
        obtemCampo = valor.Field
    case reflect.Slice, reflect.Array:
        quantidadeDeValores = valor.Len()
        obtemCampo = valor.Index
    case reflect.Map:
        for _, chave := range valor.MapKeys() {
            percorre(valor.MapIndex(chave).Interface(), fn)
        }
    }

    for i := 0; i< quantidadeDeValores; i++ {
        percorre(obtemCampo(i).Interface(), fn)
    }
}
```

No entanto, por design, não é possível obter os valores de um map por índice. Só é possível fazer isso pela *chave*, que, caramba, acaba com a nossa abstração.

## Refatoração

Como se sente agora? Parecia que essa era uma boa abstração naquele momento, mas agora o código parece um pouco bagunçado.

*Está tudo bem!* Refatoração é uma jornada e às vezes vamos cometer erros. Um ponto importante do TDD é que ele nos dá a liberdade de testar esse tipo de coisa.

Graças aos testes implmentados a cada etapa, essa situação não é irreversível de forma alguma. Vamos apenas voltar a como estava antes da refatoração.

```go
func percorre(x interface{}, fn func(entrada string)) {
	valor := obtemValor(x)

	percorreValor := func(valor reflect.Value) {
		percorre(valor.Interface(), fn)
	}

	switch valor.Kind() {
	case reflect.String:
		fn(valor.String())
	case reflect.Struct:
		for i := 0; i < valor.NumField(); i++ {
			percorreValor(valor.Field(i))
		}
	case reflect.Slice, reflect.Array:
		for i := 0; i < valor.Len(); i++ {
			percorreValor(valor.Index(i))
		}
	case reflect.Map:
		for _, chave := range valor.MapKeys() {
			percorreValor(valor.MapIndex(chave))
		}
	}
}
```

Apresentamos o `percorreValor`, que encapsula chamadas para `percorre` dentro do nosso `switch` para que só tenham que extrair os `reflect.Value` de `valor`.

### Um último problema

Lembre que maps em Go não têm ordem garantida. Logo, às vezes os testes irão falhar porque verificamos as chamadas de `fn` em uma ordem específica.

Para arrumar isso, precisaremos mover nossa verificação com os maps para um novo teste onde não nos importamos com a ordem.

```go
t.Run("com maps", func(t *testing.T) {
	mapA := map[string]string{
		"Foo": "Bar",
		"Baz": "Boz",
    }
    
	var resultado []string
	percorre(mapA, func(entrada string) {
		resultado = append(resultado, entrada)
    })
    
	verificaSeContem(t, resultado, "Bar")
	verificaSeContem(t, resultado, "Boz")
})
```

Essa é a definição de `verificaSeContem`:

```go
func verificaSeContem(t *testing.T, palheiro []string, agulha string) {
	contem := false
	for _, x := range palheiro {
		if x == agulha {
			contem = true
		}
	}
	if !contem {
		t.Errorf("esperava-se que %+v contivesse '%s', mas não continha", palheiro, agulha)
	}
}
```

## Resumo

* Apresentamos alguns dos conceitos do pacote `reflect`.
* Usamos recursão para percorrer estruturas de dados arbitrárias.
* Houve uma reflexão quanto a uma refatoração ruim, mas não há por que se preocupar muito com isso. Isso não deve ser um problema muito grande se trabalharmos com testes de forma iterativa.
* Esse capítulo só cobre um aspecto pequeno de reflexão. [O blog do Go tem um artigo excelente cobrindo mais detalhes](https://blog.golang.org/laws-of-reflection).
* Agora que você tem conhecimento sobre reflexão, faça o possível para evitá-lo.


# Sync

[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/sync)

Queremos fazer um contador que é seguro para ser usado concorrentemente.

Vamos começar com um contador não seguro e verificar se seu comportamento funciona em um ambiente com apenas uma *thread*.

Em seguida, vamos testar sua falta de segurança com várias *goroutines* tentando usar o contador dentro dos testes e consertar essa falha.

## Escreva o teste primeiro

Queremos que nossa API nos dê um método para incrementar o contador e depois recupere esse valor.

```go
func TestContador(t *testing.T) {
    t.Run("incrementar o contador 3 vezes resulta no valor 3", func(t *testing.T) {
        contador := Contador{}
        contador.Incrementa()
        contador.Incrementa()
        contador.Incrementa()

        if contador.Valor() != 3 {
		t.Errorf("resultado %d, esperado %d", contador.Valor(), 3)
	}
    })
}
```

## Tente rodar o teste

```
./sync_test.go:9:14: undefined: Contador
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Vamos definir `Contador`.

```go
type Contador struct {

}
```

Tente rodar o teste de novo e ele falhará com o seguinte erro:

```
./sync_test.go:14:10: contador.Incrementa undefined (type Contador has no field or method Incrementa)
./sync_test.go:18:13: contador.Valor undefined (type Contador has no field or method Valor)
```

Então, para finalmente fazer o teste rodar, podemos definir esses métodos:

```go
func (c *Contador) Incrementa() {

}

func (c *Contador) Valor() int {
    return 0
}
```

Agora tudo deve rodar e falhar:

```
=== RUN   TestContador
=== RUN   TestContador/incrementar_o_contador_3_vezes_resulta_no_valor_3
--- FAIL: TestContador (0.00s)
    --- FAIL: TestContador/incrementar_o_contador_3_vezes_resulta_no_valor_3 (0.00s)
        sync_test.go:27: resultado 0, esperado 3
```

## Escreva código o suficiente para fazer o teste passar

Isso deve ser simples para *experts* em Go como nós. Precisamos criar uma instância do tipo Contador e incrementá-lo com cada chamada de `Incrementa`.

```go
type Contador struct {
    valor int
}

func (c *Contador) Incrementa() {
    c.valor++
}

func (c *Contador) Valor() int {
    return c.valor
}
```

## Refatoração

Não há muito o que refatorar, mas já que iremos escrever mais testes em torno do `Contador`, vamos escrever uma pequena função de asserção `verificaContador` para que o teste fique um pouco mais legível.

```go
t.Run("incrementar o contador 3 vezes resulta no valor 3", func(t *testing.T) {
	contador := Contador{}
	contador.Incrementa()
	contador.Incrementa()
	contador.Incrementa()

	verificaContador(t, contador, 3)
})

func verificaContador(t *testing.T, resultado Contador, esperado int) {
	t.Helper()
	if resultado.Valor() != esperado {
		t.Errorf("resultado %d, esperado %d", resultado.Valor(), esperado)
	}
}
```

## Próximos passos

Isso foi muito fácil, mas agora temos um requerimento que é: o programa precisa ser seguro o suficiente para ser usado em um ambiente com acesso concorrente. Vamos precisar criar um teste para exercitar isso.

## Escreva o teste primeiro

```go
t.Run("roda concorrentemente em segurança", func(t *testing.T) {
	contagemEsperada := 1000
	contador := Contador{}

	var wg sync.WaitGroup
	wg.Add(contagemEsperada)

	for i := 0; i < contagemEsperada; i++ {
		go func(w *sync.WaitGroup) {
			contador.Incrementa()
			w.Done()
		}(&wg)
	}
	wg.Wait()

	verificaContador(t, contador, contagemEsperada)
})
```

Isso vai iterar até a nossa `contagemEsperada` e disparar uma *goroutine* para chamar `contador.Incrementa()` a cada iteração.

Estamos usando [`sync.WaitGroup`](https://golang.org/pkg/sync/#WaitGroup), que é uma maneira simples de sincronizar processos concorrentes.

> Um WaitGroup aguarda por uma coleção de *goroutines* terminar seu processamento. A *goroutine* principal faz a chamada para o `Add` definir o número de *goroutines* que serão esperadas. Então, cada uma das *goroutines* é executada e chama `Done` quando termina sua execução. Ao mesmo tempo, `Wait` pode ser usado para bloquear a execução até que todas as *goroutines* tenham terminado.

Ao esperar por `wg.Wait()` terminar sua execução antes de fazer nossas asserções, podemos ter certeza que todas as nossas *goroutines* tentaram chamar o `Incrementa` no `Contador`.

## Tente rodar o teste

```
=== RUN   TestContador/roda_concorrentemente_em_seguranca
--- FAIL: TestContador (0.00s)
    --- FAIL: TestContador/roda_concorrentemente_em_seguranca (0.00s)
        sync_test.go:26: resultado 939, esperado 1000
FAIL
```

O teste *provavelmente* vai falhar com um número diferente, mas de qualquer forma demonstra que não roda corretamente quando várias *goroutines* tentam mudar o valor do contador ao mesmo tempo.

## Escreva código o suficiente para fazer o teste passar

Uma solução simples é adicionar uma trava ao nosso `Contador`, um [`Mutex`](https://golang.org/pkg/sync/#Mutex).

> Um Mutex é uma trava de exclusão mútua. O valor zero de um Mutex é um Mutex destravado.

```go
type Contador struct {
    mu sync.Mutex
    valor int
}

func (c *Contador) Incrementa() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.valor++
}
```

Isso significa que qualquer *goroutine* chamando `Incrementa` vai receber a trava em `Contador` se for a primeira chamando essa função. Todas as outras *goroutines* vão ter que esperar por essa primeira execução até que ele esteja `Unlock`, ou destravado, antes de ganhar o acesso à instância de `Contador` alterada pela primeira chamada de função.

Agora, se você rodar o teste novamente, ele deve funcionar porque cada uma das *goroutines* tem que esperar até que seja sua vez antes de fazer alguma mudança.

## Já vi outros exemplos em que o `sync.Mutex` está embutido dentro da struct.

Você pode ver exemplos como esse:

```go
type Contador struct {
    sync.Mutex
    valor int
}
```

Há quem diga que isso torna o código um pouco mais elegante.

```go
func (c *Contador) Incrementa() {
    c.Lock()
    defer c.Unlock()
    c.valor++
}
```

Isso *parece* legal, mas, apesar de programação ser uma área altamente subjetiva, isso é **feio e errado**.

Às vezes as pessoas esquecem que tipos embutidos significam que os métodos daquele tipo se tornam *parte da interface pública*; e você geralmente não quer isso. Não se esqueçam que devemos ter muito cuidado com as nossas APIs públicas. O momento que tornamos algo público é o momento que outros códigos podem acoplar-se a ele e queremos evitar acoplamentos desnecessários.

Expôr `Lock` e `Unlock` é, no seu melhor caso, muito confuso e, no seu pior caso, potencialmente perigoso para o seu software se quem chamar o seu tipo começar a chamar esses métodos diretamente.

![Demonstração de como um usuário dessa API pode chamar erroneamente o estado da trava](https://i.imgur.com/SWYNpwm.png)

*Isso parece uma péssima ideia.*

## Copiando mutexes

Nossos testes passam, mas nosso código ainda é um pouco perigoso.

Se você rodar `go vet` no seu código, deve receber um erro similar ao seguinte:

```
sync/v2/sync_test.go:16: call of verificaContador copies lock valor: v1.Contador contains sync.Mutex
sync/v2/sync_test.go:39: verificaContador passes lock by valor: v1.Contador contains sync.Mutex
```

Uma rápida olhada na documentação do [`sync.Mutex`](https://golang.org/pkg/sync/#Mutex) nos diz o porquê:

> Um Mutex não deve ser copiado depois do primeiro uso.

Quando passamos nosso `Contador` (por valor) para `verificaContador`, ele vai tentar criar uma cópia do mutex.

Para resolver isso, devemos passar um ponteiro para o nosso `Contador`. Vamos, então, mudar a assinatura de `verificaContador`.

```go
func verificaContador(t *testing.T, resultado *Contador, esperado int)
```

Nossos testes não vão mais compilar porque estamos tentando passar um `Contador` ao invés de um `*Contador`. Para resolver isso, é melhor criar um construtor que mostra aos usuários da nossa API que seria melhor ele mesmo não inicializar seu tipo.

```go
func NovoContador() *Contador {
    return &Contador{}
}
```

Use essa função em seus testes quando for inicializar o `Contador`.

## Resumo

Falamos sobre algumas coisas do [pacote sync](https://golang.org/pkg/sync/):

* `Mutex` nos permite adicionar travas aos nossos dados
* `WaitGroup` é uma maneira de esperar as *goroutines* terminarem suas tarefas

### Quando usar travas em vez de *channels* e *goroutines*?

[Anteriormente falamos sobre *goroutines* no primeiro capítulo sobre concorrência](/aprenda-go-com-testes/main/primeiros-passos-com-go/concorrencia) que nos permite escrever código concorrente e seguro, então por que usar travas? [A wiki do Go tem uma página dedicada para esse tópico: Mutex ou Channel?](https://github.com/golang/go/wiki/MutexOrChannel)

> Um erro comum de um iniciante em Go é usar demais os *channels* e *goroutines* apenas porque é possível e/ou porque é divertido. Não tenha medo de usar um `sync.Mutex` se for uma solução melhor para o seu problema. Go é pragmático em deixar você escolher as ferramentas que melhor resolvem o seu problema e não te força em um único estilo de código.

Resumindo:

* **Use channels quando for passar a propriedade de um dado**
* **Use mutexes para gerenciar estados**

### go vet

Não se esqueça de usar `go vet` nos seus scripts de *build* porque ele pode te alertar a respeito de bugs mais sutis no seu código antes que eles atinjam seus pobres usuários.

### Não use códigos embutidos apenas porque é conveniente

* Pense a respeito do efeito que embutir códigos tem na sua API pública.
* Você *realmente* quer expôr esses métodos e ter pessoas acoplando o código próprio delas a ele?
* Mutexes podem se tornar um desastre de maneiras muito imprevisíveis e estranhas. Imagine um código inesperado destravando um mutex quando não deveria? Isso causaria erros muito estranhos que seriam muito difíceis de encontrar.


# Contexto

[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/primeiros-passos-com-go/contexto)

Softwares geralmente iniciam processos de longa duração e de uso intensivo de recursos (muitas vezes em goroutines). Se a ação que causou isso é cancelada ou falha por algum motivo, você precisa parar esses processos de forma consistente dentro da sua aplicação.

Se você não gerenciar isso, sua aplicação Go tão ágil da qual você tem tanto orgulho pode começar a ter problemas de desempenho difíceis de depurar.

Neste capítulo vamos usar o pacote `context` para nos ajudar a gerenciar processos de longa duração.

Vamos começar com um exemplo clássico de um servidor web que, quando iniciado, abre um processo de longa execução que vai buscar alguns dados para devolver em uma resposta.

Colocaremos em prática um cenário em que um usuário cancela a requisição antes que os dados possam ser recuperados e faremos com que o processo seja instruído a desistir.

Criei um código no caminho feliz para começarmos. Aqui está o código do nosso servidor.

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, store.Fetch())
    }
}
```

A função `Server` (servidor) recebe uma `Store` (armazenamento) e nos retorna um `http.HandlerFunc`. Store está definida como:

```go
type Store interface {
    Fetch() string
}
```

A função retornada chama o método `Fetch` (busca) da `Store` para obter os dados e escrevê-los na resposta.

Nós temos um stub correspondente para `Store` que usamos em um teste.

```go
type StubStore struct {
    response string
}

func (s *StubStore) Fetch() string {
    return s.response
}

func TestHandler(t *testing.T) {
    data := "olá, mundo"
    svr := Server(&StubStore{data})

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

    svr.ServeHTTP(response, request)

    if response.Body.String() != data {
        t.Errorf(`resultado "%s", esperado "%s"`, response.Body.String(), data)
    }
}
```

Agora que temos um caminho feliz, queremos fazer um cenário mais realista onde a `Store` não consiga finalizar o `Fetch` antes que o usuário cancele a requisição.

## Escreva o teste primeiro

Nosso handler precisará de uma maneira de dizer à `Store` para cancelar o trabalho, então atualize a interface.

```go
type Store interface {
    Fetch() string
    Cancel()
}
```

Precisaremos ajustar nosso spy para que leve algum tempo para retornar `data` e uma maneira de saber que foi dito para cancelar. Nós também o renomearemos para `SpyStore`, pois agora vamos observar a forma como ele é chamado. Ele terá que adicionar `Cancel` como um método para implementar a interface `Store`.

```go
type SpyStore struct {
    response string
    cancelled bool
}

func (s *SpyStore) Fetch() string {
    time.Sleep(100 * time.Millisecond)
    return s.response
}

func (s *SpyStore) Cancel() {
    s.cancelled = true
}
```

Vamos adicionar um novo teste onde cancelamos a requisição antes de 100 milissegundos e verificamos a store para ver se ela é cancelada.

```go
t.Run("avisa a store para cancelar o trabalho se a requisição for cancelada", 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 não foi avisada para cancelar")
      }
  })
```

Do blog da Google novamente:

> O pacote context fornece funções para derivar novos valores de contexto dos já existentes. Estes valores formam uma árvore: quando um contexto é cancelado, todos os contextos derivados dele também são cancelados.

É importante que você derive seus contextos para que os cancelamentos sejam propagados através da pilha de chamadas ([*call stack*](https://www.ardanlabs.com/blog/2015/01/stack-traces-in-go.html)) para uma determinada requisição.

O que fazemos é derivar um novo `cancellingCtx` da nossa requisição que nos retorna uma função `cancel`. Nós então programamos que a função seja chamada em 5 milissegundos usando `time.AfterFunc`. Por fim, usamos este novo contexto em nossa requisição chamando `request.WithContext`.

## Execute o teste

O teste falha como seria de esperar.

```go
--- FAIL: TestServer (0.00s)
    --- FAIL: TestServer/avisa_a_store_para_cancelar_o_trabalho_se_a_requisicao_for_cancelada (0.00s)
        context_test.go:62: store no foi avisada para cancelar
```

## Escreva código o suficiente para fazer o teste passar

Lembre-se de ser disciplinado com o TDD. Escreva a quantidade *mínima* de código para fazer nosso teste passar.

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        store.Cancel()
        fmt.Fprint(w, store.Fetch())
    }
}
```

Isto faz com que este teste passe, mas não parece tão bom! Certamente não deveríamos estar cancelando a `Store` antes do fetch em *cada requisição*.

Ao ser disciplinado ele destacou uma falha em nossos testes, isso é uma coisa boa!

Vamos precisar atualizar nosso teste de caminho feliz para verificar que ele não será cancelado.

```go
t.Run("retorna dados da store", func(t *testing.T) {
    store := SpyStore{response: data}
    svr := Server(&store)

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

    svr.ServeHTTP(response, request)

    if response.Body.String() != data {
        t.Errorf(`resultado "%s", esperado "%s"`, response.Body.String(), data)
    }

    if store.cancelled {
        t.Error("não deveria ter cancelado a store")
    }
})
```

Execute ambos os testes. O teste do caminho feliz deve agora estar falhando e somos forçados a fazer uma implementação mais sensata.

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()

        data := make(chan string, 1)

        go func() {
            data <- store.Fetch()
        }()

        select {
        case d := <-data:
            fmt.Fprint(w, d)
        case <-ctx.Done():
            store.Cancel()
        }
    }
}
```

O que fizemos aqui?

`context` tem um método `Done()` que retorna um canal que recebe um sinal quando o context estiver "done" (finalizado) ou "cancelled" (cancelado). Queremos ouvir esse sinal e chamar `store.Cancel` se o obtivermos, mas queremos ignorá-lo se a nossa `Store` conseguir finalizar o `Fetch` antes dele.

Para gerenciar isto, executamos o `Fetch` em uma goroutine e ele irá escrever o resultado em um novo channel `data`. Nós então usamos `select` para efetivamente correr para os dois processos assíncronos e então escrevemos uma resposta ou cancelamos com `Cancel`.

## Refatoração

Podemos refatorar um pouco o nosso código de teste fazendo métodos de verificação no nosso spy.

```go
func (s *SpyStore) assertWasCancelled() {
    s.t.Helper()
    if !s.cancelled {
        s.t.Errorf("store não foi avisada para cancelar")
    }
}

func (s *SpyStore) assertWasNotCancelled() {
    s.t.Helper()
    if s.cancelled {
        s.t.Errorf("store foi avisada para cancelar")
    }
}
```

Lembre-se de passar o `*testing.T` ao criar o spy.

```go
func TestServer(t *testing.T) {
    data := "olá, mundo"

    t.Run("retorna dados da store", func(t *testing.T) {
        store := &SpyStore{response: data, t: t}
        svr := Server(store)

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

        svr.ServeHTTP(response, request)

        if response.Body.String() != data {
            t.Errorf(`recebi "%s", quero "%s"`, response.Body.String(), data)
        }

        store.assertWasNotCancelled()
    })

    t.Run("avisa a store para cancelar o trabalho se a requisição for cancelada", func(t *testing.T) {
        store := &SpyStore{response: data, t: t}
        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)

        store.assertWasCancelled()
    })
}
```

Esta abordagem é boa, mas é idiomática?

Faz sentido para o nosso servidor web estar preocupado com o cancelamento manual da `Store`? E se a `Store` também depender de outros processos de execução lenta? Nós teremos que ter certeza que a `Store.Cancel` propagará corretamente o cancelamento para todos os seus dependentes.

Um dos pontos principais do `context` é que é uma maneira consistente de oferecer cancelamento.

[Da documentação do go](https://golang.org/pkg/context/)

> As requisições de entrada para um servidor devem criar um Context e as chamadas de saída para servidores devem aceitar um Context. A cadeia de chamadas de função entre eles deve propagar o Context, substituindo-o opcionalmente por um Context derivado criado usando WithCancel, WithDeadline, WithTimeout ou WithValue. Quando um Context é cancelado, todos os Contexts derivados dele também são cancelados.

Do blog da Google novamente:

> Na Google, exigimos que os programadores Go passem um parâmetro Context como o primeiro argumento para cada função no caminho de chamada entre requisições de entrada e saída. Isto permite que o código Go desenvolvido por muitas equipes diferentes interopere bem. Ele fornece um controle simples sobre timeouts e cancelamentos e garante que valores críticos, como credenciais de segurança, transitem corretamente pelos programas Go.

(Pare por um momento e pense nas ramificações de cada função tendo que enviar um context e a ergonomia disso.)

Se sentindo um pouco desconfortável? Bom. Vamos tentar seguir essa abordagem e, em vez disso, passar o `context` para nossa `Store` e deixá-la ser responsável. Dessa maneira, ela também pode passar o `context` para os seus dependentes e eles também podem ser responsáveis por se pararem.

## Escreva o teste primeiro

Teremos de alterar os nossos testes existentes, uma vez que as suas responsabilidades estão mudando. As únicas coisas que nosso handler é responsável agora é certificar-se que emite um contexto à `Store` em cascata (downstream) e que trata o erro que virá da `Store` quando é cancelada.

Vamos atualizar nossa interface `Store` para mostrar as novas responsabilidades.

```go
type Store interface {
    Fetch(ctx context.Context) (string, error)
}
```

Apague o código dentro do nosso handler por enquanto:

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
    }
}
```

Atualize nosso `SpyStore`:

```go
type SpyStore struct {
    response string
    t        *testing.T
}

func (s *SpyStore) Fetch(ctx context.Context) (string, error) {
    data := make(chan string, 1)

    go func() {
        var result string
        for _, c := range s.response {
            select {
            case <-ctx.Done():
                s.t.Log("spy store foi cancelado")
                return
            default:
                time.Sleep(10 * time.Millisecond)
                result += string(c)
            }
        }
        data <- result
    }()

    select {
    case <-ctx.Done():
        return "", ctx.Err()
    case res := <-data:
        return res, nil
    }
}
```

Temos que fazer nosso spy agir como um método real que funciona com o `context`.

Estamos simulando um processo lento onde construímos o resultado lentamente adicionando a string, caractere por caractere em uma goroutine. Quando a goroutine termina seu trabalho, ela escreve a string no channel `data`. A goroutine escuta o `ctx.Done` e irá parar o trabalho se um sinal for enviado nesse channel.

Finalmente o código usa outro `select` para esperar que a goroutine termine seu trabalho ou que o cancelamento ocorra.

É semelhante à nossa abordagem de antes onde usamos as primitivas de concorrência do Go para fazerem dois processos assíncronos disputarem um contra o outro para determinar o que retornamos.

Você usará uma abordagem similar ao escrever suas próprias funções e métodos que aceitam um `context`, por isso certifique-se de que está entendendo o que está acontecendo.

Nós removemos a referência ao `ctx` dos campos do `SpyStore` porque não é mais interessante para nós. Estamos estritamente testando o comportamento agora, que preferimos em comparação aos detalhes da implementação dos testes, como "você passou um determinado valor para a função `foo`".

Finalmente podemos atualizar nossos testes. Comente nosso teste de cancelamento para que possamos corrigir o teste do caminho feliz primeiro.

```go
t.Run("retorna dados da store", func(t *testing.T) {
    store := &SpyStore{response: data, t: t}
    svr := Server(store)

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

    svr.ServeHTTP(response, request)

    if response.Body.String() != data {
        t.Errorf(`resultado "%s", esperado "%s"`, response.Body.String(), data)
    }
})
```

## Execute o teste

```
=== RUN   TestServer/retorna_dados_da_store
--- FAIL: TestServer (0.00s)
    --- FAIL: TestServer/retorna_dados_da_store (0.00s)
        context_test.go:22: resultado "", esperado "olá, mundo"
```

## Escreva código o suficiente para fazer o teste passar

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        data, _ := store.Fetch(r.Context())
        fmt.Fprint(w, data)
    }
}
```

O nosso caminho feliz deve estar... feliz. Agora podemos corrigir o outro teste.

## Escreva o teste primeiro

Precisamos testar que não escrevemos qualquer tipo de resposta no caso de erro. Infelizmente o `httptest.ResponseRecorder` não tem uma maneira de descobrir isso, então teremos que usar nosso próprio spy para testar.

```go
type SpyResponseWriter struct {
    written bool
}

func (s *SpyResponseWriter) Header() http.Header {
    s.written = true
    return nil
}

func (s *SpyResponseWriter) Write([]byte) (int, error) {
    s.written = true
    return 0, errors.New("não implementado")
}

func (s *SpyResponseWriter) WriteHeader(statusCode int) {
    s.written = true
}
```

Nosso `SpyResponseWriter` implementa `http.ResponseWriter` para que possamos usá-lo no teste.

```go
t.Run("avisa a store para cancelar o trabalho se a requisição for cancelada", func(t *testing.T) {
    store := &SpyStore{response: data, t: t}
    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 := &SpyResponseWriter{}

    svr.ServeHTTP(response, request)

    if response.written {
        t.Error("uma resposta não deveria ter sido escrita")
    }
})
```

## Execute o teste

```
=== RUN   TestServer
=== RUN   TestServer/avisa_a_store_para_cancelar_o_trabalho_se_a_requisicao_for_cancelada
--- FAIL: TestServer (0.01s)
    --- FAIL: TestServer/avisa_a_store_para_cancelar_o_trabalho_se_a_requisicao_for_cancelada (0.01s)
        context_test.go:47: uma resposta não deveria ter sido escrita
```

## Escreva código o suficiente para fazer o teste passar

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        data, err := store.Fetch(r.Context())

        if err != nil {
            return // todo: registre o erro como você quiser
        }

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

Podemos ver depois disso que o código do servidor se tornou simplificado, pois não é mais explicitamente responsável pelo cancelamento. Ele simplesmente passa o `context` e confia nas funções em cascata (downstream) para respeitar qualquer cancelamento que possa ocorrer.

## Resumo

### Sobre o que falamos

* Como testar um handler HTTP que teve a requisição cancelada pelo cliente.
* Como usar o contexto para gerenciar o cancelamento.
* Como escrever uma função que aceita `context` e o usa para se cancelar usando goroutines, `select` e canais.
* Seguir as diretrizes da Google a respeito de como controlar o cancelamento propagando o contexto escopado da requisição através da sua pilha de chamadas (*call stack*).
* Como levar seu próprio spy para `http.ResponseWriter` se você precisar dele.

### E quanto ao context.Value?

[Michal Štrba](https://faiface.github.io/post/context-should-go-away-go2/) e eu temos uma opinião semelhante.

> Se você usar o ctx.Value na minha empresa (inexistente), você está demitido

Alguns engenheiros têm defendido a passagem de valores através do `context` porque *parece conveniente*.

A conveniência é muitas vezes a causa do código ruim.

O problema com `context.Values` é que ele é apenas um mapa não tipado para que você não tenha nenhum tipo de segurança e você tem que lidar com ele não realmente contendo seu valor. Você tem que criar um acoplamento de chaves de mapa de um módulo para outro e se alguém muda alguma coisa começar a quebrar.

Resumindo, **se uma função necessita de alguns valores, coloque-os como parâmetros tipados em vez de tentar obtê-los a partir de `context.Value`**. Isto torna-o estaticamente verificado e documentado para que todos o vejam.

#### Mas...

Por outro lado, pode ser útil incluir informações que sejam ortogonais a uma requisição em um contexto, como um identificador único. Potencialmente esta informação não seria necessária para todas as funções da sua pilha de chamadas (*call stack*) e tornaria as suas assinaturas funcionais muito confusas.

[Jack Lindamood diz que **Context.Value deve informar, não controlar**](https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39)

> O conteúdo do context.Value é para os mantenedores e não para os usuários. Ele nunca deve ser uma entrada necessária para resultados documentados ou esperados.

### Material adicional

* Gostei muito de ler [Context should go away for Go 2 por Michal Štrba](https://faiface.github.io/post/context-should-go-away-go2/). Seu argumento é que ter que passar o `context` em toda parte é um indicador que está apontando a uma deficiência na linguagem a respeito do cancelamento. Ele diz que seria melhor se isso fosse resolvido de alguma forma no nível de linguagem, em vez de em um nível de biblioteca. Até que isso aconteça, você precisará do `context` se quiser gerenciar processos de longa duração.
* O [blog do Go descreve ainda mais a motivação para trabalhar com `context` e tem alguns exemplos](https://blog.golang.org/context)


# Introdução

Finalizada a seção dos *Primeiros passos com Go*, você já deve possuir uma base sólida sobre os principais recursos da linguagem Go e como utilizar o TDD durante o seu processo de desenvolvimento.

Nossos próximos passos vão envolver o desenvolvimento de uma aplicação. Nessa seção, todo capitulo irá depender da funcionalidade implementada pelo seu antecessor, por isso evite pulá-los.

Aqui novos conceitos serão introduzidos para facilitar a escrita de grandes aplicações e a maior parte desse projeto será realizada utilizando bibliotecas padrões da linguagem Go.

Até o final dessa seção você deve ter obtido um entendimento sólido de como escrever aplicações em Go com o apoio de testes.

* [Servidor HTTP](/aprenda-go-com-testes/main/criando-uma-aplicacao/servidor-http) - Vamos criar uma API que aceita requisições HTTP.
* [Respostas em JSON e roteamentos](https://github.com/larien/learn-go-with-tests/blob/main/criando-uma-aplicacao/json.md) - Iremos evoluir nossa API para retornar objetos JSON e vamos explorar como fazer roteamentos.
* [IO ](https://github.com/larien/learn-go-with-tests/blob/main/criando-uma-aplicacao/io.md)- Vamos salvar e ler dados de arquivos. Também vamos ordenar esses dados.
* [Linha de comando](https://github.com/larien/learn-go-with-tests/blob/main/criando-uma-aplicacao/linha-de-comando.md) - Vamos criar uma aplicação que vai ser utilizada por linha de comando no terminal, para entendermos como podemos suportar múltiplas plataformas.
* [Eventos](https://github.com/larien/learn-go-with-tests/blob/main/criando-uma-aplicacao/time.md) - Vamos agendar alguns eventos de processamento que irão acontecer dependendo do horário que usuário utilizou a aplicação.


# Servidor HTTP

[**Você encontra todo o código-fonte para este capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/servidor-http)

Você recebeu o desafio de criar um servidor web para que usuários possam acompanhar quantas partidas os jogadores venceram.

* `GET /jogadores/{nome}` deve retornar um número indicando o número total de vitórias
* `POST /jogadores/{nome}` deve registrar uma vitória para este nome de jogador, incrementando a cada nova chamada de submissão de dados (método HTTP `POST`).

Vamos seguir com a abordagem do Desenvolvimento Orientado a Testes, criando software que funciona o mais rápido possível, e a cada ciclo fazendo pequenas melhorias até uma solução completa. Com essa abordagem, nós:

* Mantemos pequeno o escopo do problema em qualquer momento
* Não perdemos o foco por pensar em muito detalhes
* Se ficamos emperrados ou perdidos, podemos voltar para uma versão anterior do código sem perder muito trabalho.

## Vermelho, verde, refatore

Por todo o livro, enfatizamos o processo Desenvolvimento Orientado a Testes de escrever um teste e ver a falha (vermelho), escrever a *menor* quantidade de código para fazer o teste passar/funcionar (verde), e então fazemos a reescrita (refatoração).

A disciplina de escrever a menor quantidade de código é importante para garantir a segurança que o Desenvolvimento Orientado a Testes proporciona. Você deve se empenhar em sair do *vermelho* o quanto antes.

Kent Beck descreve essa prática como:

> Faça o teste passar rapidamente, cometendo quaisquer pecados necessários nesse processo.

E você pode cometer estes pecados porque vamos reescrever o código logo depois, com a segurança garantida pelos testes.

### E se você não fizer assim?

Quanto mais alterações você fizer enquanto seu código estiver em *vermelho*, maiores as chances de você adicionar problemas, não cobertos por testes.

A ideia é escrever iterativamente código útil em pequenos passos, guiados pelos testes, para que você não perca foco no objetivo principal.

### A galinha e o ovo

Como podemos construir isso de forma incremental? Não podemos obter um jogador (método HTTP `GET`) sem tê-lo registrado anteriormente, e parece complicado saber se a chamada ao método HTTP `POST` funcionou sem a chamada ao método HTTP `GET` já implementado.

E é nesse ponto que o uso de *classes com valores predefinidos* vai nos ajudar.

(Nota de tradução: No original, é usada a expressão *mocking*, que significa "zombar", "fazer piada" ou "enganar". Em programação, *mocking* significa criar *algo*, como uma classe ou função, que retorna os valores esperados de forma predefinida.)

* a implementação que responde ao método HTTP `GET` precisa de uma *coisa* `ArmazenamentoJogador` para obter pontuações de um nome de jogador. Isso deve ser uma interface, para que, ao executar os testes, seja possível criar um código simples de esboço para testar o código sem precisar, neste momento, implementar o código final que será usado para armazenar os dados.
* para o método HTTP `POST`, podemos *inspecionar* as chamadas feitas a `ArmazenamentoJogador` para ter certeza de que os dados são armazenados corretamente. Nossa implementação de gravação dos dados não estará vinculada à busca dos dados.
* para ver código rodando rapidamente vamos fazer uma implementação simples de armazenamento dos dados na memória, e depois podemos criar uma implementação que dá suporte ao mecanismo de armazenamento de preferência.

## Escrevendo o teste primeiro

Podemos escrever um teste e fazer funcionar retornando um valor predeterminado para nos ajudar a começar. Kent Beck se refere a isso como "Fazer de conta". Uma vez que temos um teste funcionando podemos escrever mais testes que nos ajudem a remover este valor predeterminado (constante).

Com este pequeno passo, nós começamos a ter uma estrutura inicial para o projeto funcionando corretamente, sem nos preocuparmos demais com a lógica da aplicação.

Para criar um servidor web (uma aplicação que recebe chamadas via protocolo HTTP) em Go, você vai chamar, usualmente, a função [ListenAndServe](https://golang.org/pkg/net/http/#ListenAndServe).

```go
func ListenAndServe(endereco string, tratador Handler) error
```

Isso vai iniciar um servidor web *escutando* em uma porta, criando uma gorotina para cada requisição, e repassando para um *Tratador*, que é representado pela interface [`Handler`](https://golang.org/pkg/net/http/#Handler), usada para receber a requisição e avaliar o que fazer com os dados recebidos.

```go
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
```

Esta interface define uma única função que espera dois argumentos, o primeiro que indica onde *escrevemos a resposta* e o outro com a requisição HTTP que nos foi enviada.

Vamos criar o primeiro arquivo, `servidor_test.go` e escrever um teste para a função `ServidorJogador` que recebe estes dois argumentos. A requisição enviada serve para obter a pontuação de um Nome de Jogador, que esperamos que seja `"20"`.

```go
func TestObterJogadores(t *testing.T) {
    t.Run("retornar resultado de Maria", func(t *testing.T) {
        requisicao, _ := http.NewRequest(http.MethodGet, "/jogadores/Maria", nil)
        resposta := httptest.NewRecorder()

        ServidorJogador(resposta, requisicao)

        recebido := resposta.Body.String()
        esperado := "20"

        if recebido != esperado {
            t.Errorf("recebido '%s', esperado '%s'", recebido, esperado)
        }
    })
}
```

Para testar nosso servidor, vamos precisar de uma *Requisição* (`Request`) para enviar ao servidor, e então queremos *inspecionar* o que o nosso Tratador escreve para o `ResponseWriter`.

* Nós usamos o `http.NewRequest` para criar uma requisição. O primeiro argumento é o *método* da requisição e o segundo é o caminho da requisição. O valor `nil` para o segundo argumento corresponde ao corpo (*body*) da requisição, que não precisamos definir para este teste.
* `net/http/httptest` já tem um *inspecionador* criado para nós, chamado `ResponseRecorder` (*GravadorDeResposta*), então podemos usá-lo. Este possui muitos métodos úteis para inspecionar o que foi escrito como resposta.

## Tente rodar o teste

`./servidor_test.go:13:2: undefined: ServidorJogador`

## Escreva a quantidade mínima de código para o que teste passe e verifique a falha indicada na responta do teste

O compilador está aqui para ajudar, ouça o que ele diz.

Crie o arquivo `servidor.go`, e nele a função `ServidorJogador`

```go
func ServidorJogador() {}
```

Tente novamente

```
./servidor_test.go:13:14: too many arguments in call to ServidorJogador
    have (*httptest.ResponseRecorder, *http.Request)
    want ()
```

Adicione os argumentos à função:

```go
import "net/http"

func ServidorJogador(w http.ResponseWriter, r *http.Request) {

}
```

Agora o código compila, e o teste falha.

```
--- FAIL: TestObterJogadores (0.00s)
    --- FAIL: TestObterJogadores/retornar_resultado_de_Maria (0.00s)
        servidor_test.go:20: recebido '', esperado '20'
```

## Escreva código suficiente para fazer o teste funcionar

Do capítulo sobre injeção de dependências, falamos sobre servidores HTTP com a função `Greet`. Aprendemos que a função `ResponseWriter` também implementa a interface `Writer` do pacote io, então podemos usar `fmt.Fprint` para enviar strings como respostas HTTP.

```go
func ServidorJogador(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "20")
}
```

O teste agora deve funcionar.

## Complete a estrutura

Nós queremos converter o código acima em uma aplicação. Isso é importante porque

* Teremos *software funcionando*; não queremos escrever testes apenas por escrever, e é bom ver código que funciona.
* Conforme refatoramos o código, é provável que mudaremos a estrutura do programa. Nós queremos garantir que isso é refletido em nossa aplicação também, como parte da abordagem incremental.

Crie um novo arquivo `main.go` para nossa aplicação, com o código abaixo.

```go
package main

import (
    "log"
    "net/http"
)

func main() {
    tratador := http.HandlerFunc(ServidorJogador)
    if err := http.ListenAndServe(":5000", tratador); err != nil {
        log.Fatalf("não foi possível escutar na porta 5000 %v", err)
    }
}
```

Para executar, execute o comando `go build -o programa`, que vai pegar todos os arquivos terminados em `.go` neste diretório e construir seu programa. E então você pode executar o programa rodando `./programa`.

### `http.HandlerFunc`

Anteriormente, vimos que precisamos implementar a interface `Handler` para criar um servidor. *Normalmente* fazemos isso criando um estrutura (`struct`) e fazendo com que esta implemente a interface. No entanto, mesmo que o comum seja utilizar as *estruturas* para armazenar dados, *nesse momento* não armazenamos um estado, então não parece certo criar uma *estrutura* para isso.

Usar a função [HandlerFunc](https://golang.org/pkg/net/http/#HandlerFunc) nos ajuda a resolver este problema.

> O tipo HandlerFunc é um adaptador que permite usar funções comuns como tratadores (*handlers*). Se *f* é uma função com a assinatura adequada, HandlerFunc(f) é um *Handler* que chama *f*.

```go
type HandlerFunc func(ResponseWriter, *Request)
```

Então usamos essa construção para adaptar a função `ServidorJogador`, fazendo com que esteja de acordo com a interface `Handler`.

### `http.ListenAndServe(":5000"...)`

`ListenAndServe` recebe como parâmetro um número de porta para escutar em um *tratador* (`Handler`). Se a porta já estiver sendo usada, será retornado um *erro* (`error`) para que, usando um comando `if`, possamos capturar esse erro e informar o problema para o usuário.

O que vamos fazer agora é escrever *outro* teste para nos forçar a fazer uma mudança para tentar nos afastar do valor predefinido.

## Escreva o teste primeiro

Vamos adicionar outro subteste aos nossos testes, que tenta obter a pontuação de um jogador diferente, o que causará um problema em nossa implementação que usa um código predefinido.

```go
t.Run("retornar resultado de Pedro", func(t *testing.T) {
    requisicao, _ := http.NewRequest(http.MethodGet, "/jogadores/Pedro", nil)
    resposta := httptest.NewRecorder()

    ServidorJogador(resposta, requisicao)

    recebido := resposta.Body.String()
    esperado := "10"

    if recebido != esperado {
        t.Errorf("recebido '%s', esperado '%s'", recebido, esperado)
    }
})
```

Você deve estar pensando:

> Certamente precisamos de algum tipo de armazenamento para controlar qual jogador recebe qual pontuação. É estranho que os valores sejam predefinidos em nossos testes.

Lembre-se de que estamos apenas tentando dar os menores passos possíveis; e por isso estamos, nesse momento, tentando invalidar o valor da constante.

## Tente rodar o próximo teste

```
=== RUN   TestObterJogadores
=== RUN   TestObterJogadores/retornar_resultado_de_Maria
=== RUN   TestObterJogadores/retornar_resultado_de_Pedro
    TestObterJogadores/retornar_resultado_de_Pedro: servidor_test.go:34: recebido '20', esperado '10'
--- FAIL: TestObterJogadores (0.00s)
    --- PASS: TestObterJogadores/retornar_resultado_de_Maria (0.00s)
    --- FAIL: TestObterJogadores/retornar_resultado_de_Pedro (0.00s)
```

## Escreva código suficiente para fazer passar

```go
func ServidorJogador(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    if jogador == "Maria" {
        fmt.Fprint(w, "20")
        return
    }

    if jogador == "Pedro" {
        fmt.Fprint(w, "10")
        return
    }
}
```

Este teste nos forçou a olhar para a URL da requisição e tomar uma decisão. Embora ainda estamos pensando em como armazenar os dados do jogador e as interfaces, na verdade o próximo passo a ser dado está relacionado ao *roteamento* (*routing*).

Se tivéssemos começado com o código de armazenamento dos dados, a quantidade de alterações que precisaríamos fazer seria muito grande. **Este é um pequeno passo em relação ao nosso objetivo final e foi guiado pelos testes**.

Estamos resistindo, nesse momento, à tentação de usar alguma biblioteca de roteamento, e queremos apenas dar o menor passo para fazer nossos testes funcionarem.

`r.URL.Path` retorna o caminho da requisição, e então usamos a sintaxe de slice para obter a parte final, depois de `/jogadores/`. Não é o recomendado por não ser muito robusto, mas resolve o problema por enquanto.

## Refatorar

Podemos simplificar a `ServidorJogador` separando a parte de obtenção da pontuação em uma função.

```go
func ServidorJogador(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    fmt.Fprint(w, ObterPontuacaoJogador(jogador))
}

func ObterPontuacaoJogador(nome string) string {
    if nome == "Maria" {
        return "20"
    }

    if nome == "Pedro" {
        return "10"
    }

    return ""
}
```

E podemos eliminar as repetições de parte do código dos testes montando algumas funções auxiliares("*helpers*"):

```go
func TestObterJogadores(t *testing.T) {
    t.Run("retornar resultado de Maria", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Maria")
        resposta := httptest.NewRecorder()

        ServidorJogador(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "20")
    })

    t.Run("returns Pedro's score", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Pedro")
        resposta := httptest.NewRecorder()

        ServidorJogador(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "10")
    })
}

func novaRequisicaoObterPontuacao(nome string) *http.Request {
    requisicao, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/jogadores/%s", nome), nil)
    return requisicao
}

func verificarCorpoRequisicao(t *testing.T, recebido, esperado string) {
    t.Helper()
    if recebido != esperado {
        t.Errorf("corpo da requisição é inválido, obtive '%s' esperava '%s'", recebido, esperado)
    }
}
```

Ainda assim, não estamos felizes. Não parece correto que o servidor saiba as pontuações.

Mas nossa refatoração nos mostra claramente o que fazer.

Nós movemos o cálculo de pontuação para fora do código principal que trata a requisição (*handler*) para uma função `ObterPontuacaoJogador`. Isso parece ser o lugar correto para isolar as responsabilidades usando interfaces.

Vamos alterar, em `servidor.go`, a função que refatoramos para ser uma interface:

```go
type ArmazenamentoJogador interface {
    ObterPontuacaoJogador(nome string) int
}
```

Para que o `ServidorJogador` consiga usar o `ArmazenamentoJogador`, é necessário ter uma referência a ele. Agora nos parece o momento certo para alterar nossa arquitetura, e nosso `ServidorJogador` agora se torna uma estrutura (`struct`).

```go
type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
}
```

E agora, vamos implementar a interface do *tratador* (`Handler`) adicionando um método à nossa nova estrutura `ServidorJogador` e adicionado neste método o código existente.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   jogador  := r.URL.Path[len("/jogadores/"):]
    fmt.Fprint(w, s.armazenamento.ObterPontuacaoJogador(jogador))
}
```

Outra alteração a fazer: agora usamos a `armazenamento.ObterPontuacaoJogador` para obter a pontuação, ao invés da função local definida anteriormente (e que podemos remover).

Abaixo, a listagem completa do servidor (arquivo `servidor.go`):

```go
type ArmazenamentoJogador interface {
	ObterPontuacaoJogador(nome string) int
}

type ServidorJogador struct {
	armazenamento ArmazenamentoJogador
}

func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	jogador := r.URL.Path[len("/jogadores/"):]
	fmt.Fprint(w, s.armazenamento.ObterPontuacaoJogador(jogador))
}
```

### Ajustar os problemas

Fizemos muitas mudanças, e sabemos que nossos testes não irão funcionar e a compilação deixou de funcionar nesse momento; mas relaxe, e deixe o compilador fazer o trabalho.

`./main.go:9:58: type ServidorJogador is not an expression`

Precisamos mudar os nossos testes, que agora devem criar uma nova instância de `ServidorJogador` e então chamar o método `ServeHTTP`.

```go
func TestObterJogadores(t *testing.T) {
    servidor := &ServidorJogador{}

    t.Run("retorna a pontuação de Maria", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Maria")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "20")
    })

    t.Run("retorna a pontuação de Pedro", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Pedro")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "10")
    })
}
```

Perceba que ainda não nos preocupamos, *por enquanto*, com o armazenamento dos dados, nós apenas queremos a compilação funcionando o quanto antes.

Você deve ter o hábito de priorizar, *sempre*, código que compila antes de ter código que passa nos testes.

Adicionando mais funcionalidades (como códigos de esboço de armazenamento) a um código que não ainda não compila, nos arriscamos a ter, potencialmente, *mais* problemas de compilação.

Agora `main.go` não vai compilar pelas mesmas razões.

```go
func main() {
	servidor := &ServidorJogador{}

	if err := http.ListenAndServe(":5000", servidor); err != nil {
		log.Fatalf("não foi possível escutar na porta 5000 %v", err)
	}
}
```

Agora tudo compila, mas os testes falham.

```
--- FAIL: TestObterJogadores (0.00s)
    --- FAIL: TestObterJogadores/retorna_pontucao_de_Maria (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
```

Isso porque não passamos um `ArmazenamentoJogador` em nossos testes. Precisamos fazer, no arquivo `servidor_test.go` um código de esboço para nos ajudar.

```go
type EsbocoArmazenamentoJogador struct {
	pontuacoes map[string]int
}

func (e *EsbocoArmazenamentoJogador) ObterPontuacaoJogador(nome string) int {
	pontuacao := e.pontuacoes[nome]
	return pontuacao
}
```

Um *mapa* (`map`) é um jeito simples e rápido de fazer um armazenamento chave/valor para os nossos testes. Agora vamos criar um desses armazenamentos para os nossos testes e inserir `ServidorJogador`.

```go
func TestObterJogadores(t *testing.T) {
	armazenamento := EsbocoArmazenamentoJogador{
		map[string]int{
			"Maria": 20,
			"Pedro": 10,
		},
	}
	servidor := &ServidorJogador{&armazenamento}

	t.Run("retorna pontuacao de Maria", func(t *testing.T) {
		requisicao := novaRequisicaoObterPontuacao("Maria")
		resposta := httptest.NewRecorder()

		servidor.ServeHTTP(resposta, requisicao)

		verificarCorpoRequisicao(t, resposta.Body.String(), "20")
	})

	t.Run("retorna pontuacao de Pedro", func(t *testing.T) {
		requisicao := novaRequisicaoObterPontuacao("Pedro")
		resposta := httptest.NewRecorder()

		servidor.ServeHTTP(resposta, requisicao)

		verificarCorpoRequisicao(t, resposta.Body.String(), "10")
	})
}
```

Nossos testes agora passam, e parecem melhores. Agora a *intenção* do nosso código é clara, por conta da adição do armazenamento. Estamos dizendo a quem lê o código que, por termos *este dado em um `ArmazenamentoJogador`*, quando você o usar com um `ServidorJogador` você deve obter as respostas definidas.

### Rodar a aplicação

Agora que nossos testes estão passando, a última coisa que precisamos fazer para completar a refatoração é verificar se a aplicação está funcionando. O programa deve iniciar, mas você vai receber uma mensagem horrível se tentar acessar o servidor em `http://localhost:5000/jogadores/Maria`.

E a razão pra isso é: não informamos um `ArmazenamentoJogador`.

Precisamos implementar um. No entanto, isso é difícil no momento, já que não estamos armazenando nenhum dado significativo e por isso vamos usar um valor predefinido, por enquanto. Vamos alterar na `main.go`:

```go
type ArmazenamentoJogadorEmMemoria struct{}

func (a *ArmazenamentoJogadorEmMemoria) ObterPontuacaoJogador(nome string) int {
    return 123
}

func main() {
    server := &ServidorJogador{&ArmazenamentoJogadorEmMemoria{}}

    if err := http.ListenAndServe(":5000", server); err != nil {
        log.Fatalf("não foi possível escutar na porta 5000 %v", err)
    }
}
```

Se você rodar novamente o `go build` e acessar a mesma URL você deve receber `"123"`. Não é fantástico, mas até armazenarmos os dados, é o melhor que podemos fazer.

Temos algumas opções para decidir o que fazer agora:

* Tratar o cenário onde o jogador não existe
* Tratar o cenário da chamado ao método HTTP `POST` em `/jogadores/{nome}`
* Não foi particularmente interessante perceber que nossa aplicação principal iniciou mas não funcionou. Tivemos que testar manualmente para ver o problema

Enquanto o cenário do tratamento ao método HTTP `POST` nos deixa mais perto do "caminho ideal", eu sinto que vai ser mais fácil atacar o cenário de "jogador não existente" antes, já que estamos neste assunto. Veremos os outros itens posteriormente.

## Escreva o teste primeiro

Adicione o cenário de um jogador inexistente aos nossos testes:

```go
t.Run("retorna 404 para jogador não encontrado", func(t *testing.T) {
    requisicao := novaRequisicaoObterPontuacao("Jorge")
    resposta := httptest.NewRecorder()

    server.ServeHTTP(resposta, requisicao)

    recebido := resposta.Code
    esperado := http.StatusNotFound

    if recebido != esperado {
        t.Errorf("recebido status %d esperado %d", recebido, esperado)
    }
})
```

## Tente rodar o teste

```
--- FAIL: TestObterJogadores (0.00s)
    --- FAIL: TestObterJogadores/retorna_404_para_jogador_não_encontrado (0.00s)
        servidor_test.go:56: recebido status 200 esperado 404
```

## Escreva código necessário para que o teste funcione

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    w.WriteHeader(http.StatusNotFound)

    fmt.Fprint(w, s.armazenamento.ObterPontuacaoJogador(jogador))
}
```

Às vezes eu me incomodo quando os defensores do Desenvolvimento Orientado a Testes dizem "tenha certeza de você escreveu apenas a mínima quantidade de código para fazer o teste funcionar", porque isso me parece muito pedante.

Mas este cenário ilustra muito bem o que querem dizer. Eu fiz o mínimo (sabendo que não era a implementação correta), que foi retornar um `StatusNotFound` em **todas as respostas**, mas todos os nossos testes estão passando!

**Implementando o mínimo para que os testes passem vai evidenciar as lacunas nos testes**. Em nosso caso, nós não estamos validando que devemos receber um `StatusOK` quando jogadores *existem* em nosso armazenamento.

Atualize os outros dois testes para validar o retorno e corrija o código.

Eis os novos testes:

```go
func TestObterJogadores(t *testing.T) {
	armazenamento := EsbocoArmazenamentoJogador{
		map[string]int{
			"Maria": 20,
			"Pedro": 10,
		},
	}
	servidor := &ServidorJogador{&armazenamento}

	t.Run("retorna pontuacao de Maria", func(t *testing.T) {
		requisicao := novaRequisicaoObterPontuacao("Maria")
		resposta := httptest.NewRecorder()

		servidor.ServeHTTP(resposta, requisicao)

		verificarRespostaCodigoStatus(t, resposta.Code, http.StatusOK)
		verificarCorpoRequisicao(t, resposta.Body.String(), "20")
	})

	t.Run("retorna pontuacao de Pedro", func(t *testing.T) {
		requisicao := novaRequisicaoObterPontuacao("Pedro")
		resposta := httptest.NewRecorder()

		servidor.ServeHTTP(resposta, requisicao)

		verificarRespostaCodigoStatus(t, resposta.Code, http.StatusOK)
		verificarCorpoRequisicao(t, resposta.Body.String(), "10")
	})

	t.Run("retorna 404 para jogador não encontrado", func(t *testing.T) {
		requisicao := novaRequisicaoObterPontuacao("Jorge")
		resposta := httptest.NewRecorder()

		servidor.ServeHTTP(resposta, requisicao)

		recebido := resposta.Code
		esperado := http.StatusNotFound

		if recebido != esperado {
			t.Errorf("recebido status %d esperado %d", recebido, esperado)
		}
	})
}

func novaRequisicaoObterPontuacao(nome string) *http.Request {
	req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/jogadores/%s", nome), nil)
	return req
}

func verificarCorpoRequisicao(t *testing.T, recebido, esperado string) {
	t.Helper()
	if recebido != esperado {
		t.Errorf("corpo da requisição é inválido, recebido '%s' esperado '%s'", recebido, esperado)
	}
}

func verificarRespostaCodigoStatus(t *testing.T, recebido, esperado int) {
	t.Helper()
	if recebido != esperado {
		t.Errorf("não recebeu código de status HTTP esperado, recebido %d, esperado %d", recebido, esperado)
	}
}
```

Estamos verificando o `status` (código de retorno HTTP) em todos os nossos testes, por isso existe a função auxiliar `verificarRespostaCodigoStatus` para ajudar com isso.

Agora os primeiros dois testes falham porque o código de status recebido é 404, ao invés do esperado 200. Então vamos corrigir o `ServidorJogador` para que retorne *não encontrado* (HTTP status 404) se a pontuação for 0.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	jogador := r.URL.Path[len("/jogadores/"):]

	pontuacao := s.armazenamento.ObterPontuacaoJogador(jogador)

	if pontuacao == 0 {
		w.WriteHeader(http.StatusNotFound)
	}

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

### Armazenando pontuações

Agora que podemos obter pontuações de um armazenamento, também podemos armazenar novas pontuações.

## Escreva os testes primeiro

```go
func TestArmazenamentoVitorias(t *testing.T) {
	armazenamento := EsbocoArmazenamentoJogador{
		map[string]int{},
	}
	servidor := &ServidorJogador{&armazenamento}

	t.Run("retorna status 'aceito' para chamadas ao método POST", func(t *testing.T) {
		requisicao, _ := http.NewRequest(http.MethodPost, "/jogadores/Maria", nil)
		resposta := httptest.NewRecorder()

		servidor.ServeHTTP(resposta, requisicao)

		verificarRespostaCodigoStatus(t, resposta.Code, http.StatusAccepted)
	})
}
```

Inicialmente vamos verificar se obtemos o código de status HTTP correto ao fazer a requisição em uma rota específica usando o método POST. Isso nos permite preparar o caminho da funcionalidade que aceita um tipo diferente de requisição, e tratar de forma diferente a requisição para `GET /jogadores/{nome}`. Uma vez que isso funcione como esperado, podemos começar a testar a interação do nosso tratador (*handler*) com o armazenamento.

## Tente rodar o teste

```
--- FAIL: TestArmazenamentoVitorias (0.00s)
    --- FAIL: TestArmazenamentoVitorias/retorna_status_'aceito'_para_chamadas_ao_método_POST (0.00s)
        servidor_test.go:75: não recebeu código de status HTTP esperado, recebido 404, esperado 202
```

## Escreva código suficiente pra fazer passar

Lembre-se que estamos cometendo pecados deliberadamente, então um comando `if` para identificar o método da requisição vai resolver o problema.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	if r.Method == http.MethodPost {
		w.WriteHeader(http.StatusAccepted)
		return
	}

	jogador := r.URL.Path[len("/jogadores/"):]

	pontuacao := s.armazenamento.ObterPontuacaoJogador(jogador)

	if pontuacao == 0 {
		w.WriteHeader(http.StatusNotFound)
	}

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

## Refatorar

O tratador parece um pouco bagunçado agora. Vamos separar o código para ficar simples de entender e isolar as diferentes funcionalidades em novas funções.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	switch r.Method {
	case http.MethodPost:
		s.registrarVitoria(w)
	case http.MethodGet:
		s.mostrarPontuacao(w, r)
	}
}

func (s *ServidorJogador) mostrarPontuacao(w http.ResponseWriter, r *http.Request) {
	jogador := r.URL.Path[len("/jogadores/"):]

	pontuacao := s.armazenamento.ObterPontuacaoJogador(jogador)

	if pontuacao == 0 {
		w.WriteHeader(http.StatusNotFound)
	}

	fmt.Fprint(w, pontuacao)
}

func (s *ServidorJogador) registrarVitoria(w http.ResponseWriter) {
	w.WriteHeader(http.StatusAccepted)
}
```

Isso faz com que a responsabilidade de roteamento do `ServeHTTP` esteja mais clara; e também permite que, em nossas próximas iterações, o código para armazenamento possa estar dentro de `registrarVitoria`.

Agora, queremos verificar que, quando fazemos a chamada `POST` a `/jogadores/{nome}`, nosso `ArmazenamentoJogador` registra a vitória.

## Escreva primeiro o teste

Vamos implementar isso estendendo o `EsbocoArmazenamentoJogador` com um novo método `GravarVitoria` e então inspecionar as chamadas.

```go
type EsbocoArmazenamentoJogador struct {
	pontuacoes        map[string]int
	registrosVitorias []string
}

func (e *EsbocoArmazenamentoJogador) ObterPontuacaoJogador(nome string) int {
	pontuacao := e.pontuacoes[nome]
	return pontuacao
}

func (e *EsbocoArmazenamentoJogador) RegistrarVitoria(nome string) {
	e.registrosVitorias = append(e.registrosVitorias, nome)
}
```

Agora, para começar, estendemos o teste para verificar a quantidade de chamadas:

```go
func TestArmazenamentoVitorias(t *testing.T) {
	armazenamento := EsbocoArmazenamentoJogador{
		map[string]int{},
		nil,
	}
	servidor := &ServidorJogador{&armazenamento}

	t.Run("registra vitorias na chamada ao método HTTP POST", func(t *testing.T) {
		requisicao := novaRequisicaoRegistrarVitoriaPost("Maria")
		resposta := httptest.NewRecorder()

		servidor.ServeHTTP(resposta, requisicao)

		verificarRespostaCodigoStatus(t, resposta.Code, http.StatusAccepted)

		if len(armazenamento.registrosVitorias) != 1 {
			t.Errorf("verifiquei %d chamadas a RegistrarVitoria, esperava %d", len(armazenamento.registrosVitorias), 1)
		}
	})
}

func novaRequisicaoRegistrarVitoriaPost(nome string) *http.Request {
	requisicao, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/jogadores/%s", nome), nil)
	return requisicao
}
```

## Tente rodar o teste

```
./servidor_test.go:26:17: too few values in EsbocoArmazenamentoJogador literal
./servidor_test.go:70:17: too few values in EsbocoArmazenamentoJogador literal
```

## Escreva a mínima quantidade de código para a execução do teste e verifique a falha indicada no retorno

Como adicionamos um campo, precisamos atualizar o código onde criamos o `EsbocoArmazenamentoJogador`

```go
armazenamento := EsbocoArmazenamentoJogador{
    map[string]int{},
    nil,
}
```

```
--- FAIL: TestArmazenamentoVitorias (0.00s)
    --- FAIL: TestArmazenamentoVitorias/#00 (0.00s)
        servidor_test.go:85: verifiquei 0 chamadas a RegistrarVitoria, esperava 1
```

## Escreva código suficiente para o teste passar

Como estamos apenas verificando o número de chamadas, e não seus valores específicos, nossa iteração inicial é um pouco menor.

Para conseguir invocar a `RegistrarVitoria`, precisamos atualizar a definição de `ArmazenamentoJogador` para que o `ServidorJogador` funcione como esperado.

```go
type ArmazenamentoJogador interface {
    ObterPontuacaoJogador(nome string) int
    RegistrarVitoria(nome string)
}
```

E, ao fazer isso, `main` não compila mais

```
./main.go:15:29: cannot use &ArmazenamentoJogadorEmMemoria literal (type *ArmazenamentoJogadorEmMemoria) as type ArmazenamentoJogador in field value:
        *ArmazenamentoJogadorEmMemoria does not implement ArmazenamentoJogador (missing RegistrarVitoria method)
```

O compilador nos informa o que está errado. Vamos alterar `ArmazenamentoJogadorEmMemoria`, adicionando esse método.

```go
type ArmazenamentoJogadorEmMemoria struct{}

func (s *ArmazenamentoJogadorEmMemoria) RegistrarVitoria(nome string) {}
```

Com essa alteração, o código volta a compilar - mas os testes ainda falham.

Agora que `ArmazenamentoJogador` tem o método `GravarVitoria`, podemos chamar de dentro do nosso `ServidorJogador`

```go
func (s *ServidorJogador) registrarVitoria(w http.ResponseWriter) {
    s.armazenamento.GravarVitoria("Marcela")
    w.WriteHeader(http.StatusAccepted)
}
```

Rode os testes e devem estar funcionando sem erros! Claro, `"Marcela"` não é bem o que queremos enviar para `RegistrarVitoria`, então vamos ajustar os testes.

## Escreva os testes primeiro

```go
t.Run("registra vitorias na chamada ao método HTTP POST", func(t *testing.T) {
    jogador := "Maria"

    requisicao := novaRequisicaoRegistrarVitoriaPost(jogador)
    resposta := httptest.NewRecorder()

    servidor.ServeHTTP(resposta, requisicao)

    verificarRespostaCodigoStatus(t, resposta.Code, http.StatusAccepted)

    if len(armazenamento.registrosVitorias) != 1 {
        t.Errorf("verifiquei %d chamadas a RegistrarVitoria, esperava %d", len(armazenamento.registrosVitorias), 1)
    }

    if armazenamento.registrosVitorias[0] != jogador {
        t.Errorf("não registrou o vencedor corretamente, recebi '%s', esperava '%s'", armazenamento.registrosVitorias[0], jogador)
    }
})
```

Agora sabemos que existe um elemento no slice `registrosVitorias`, e então podemos acessar, sem erros, o primeiro elemento e verificar se é igual a `jogador`.

## Tente rodar o teste

```
--- FAIL: TestArmazenamentoVitorias (0.00s)
    --- FAIL: TestArmazenamentoVitorias/registra_vitorias_na_chamada_ao_método_HTTP_POST (0.00s)
        servidor_test.go:91: não registrou o vencedor corretamente, recebi 'Marcela', esperava 'Maria'
```

## Escreva código suficiente para o teste passar

```go
func (s *ServidorJogador) registrarVitoria(w http.ResponseWriter, r *http.Request) {
	jogador := r.URL.Path[len("/jogadores/"):]
	s.armazenamento.RegistrarVitoria(jogador)
	w.WriteHeader(http.StatusAccepted)
}
```

Mudamos `registrarVitoria` para obter a `http.Request`, e assim conseguir extrair o nome do jogador da URL. Com o nome, podemos chamar o `armazenamento` com o valor correto para fazer os testes passarem.

## Refatorar

Podemos eliminar repetições no código, porque estamos obtendo o nome do jogador do mesmo jeito em dois lugares diferentes.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	jogador := r.URL.Path[len("/jogadores/"):]

	switch r.Method {
	case http.MethodPost:
		s.registrarVitoria(w, jogador)
	case http.MethodGet:
		s.mostrarPontuacao(w, jogador)
	}
}

func (s *ServidorJogador) mostrarPontuacao(w http.ResponseWriter, jogador string) {
	pontuacao := s.armazenamento.ObterPontuacaoJogador(jogador)

	if pontuacao == 0 {
		w.WriteHeader(http.StatusNotFound)
	}

	fmt.Fprint(w, pontuacao)
}

func (s *ServidorJogador) registrarVitoria(w http.ResponseWriter, jogador string) {
	s.armazenamento.RegistrarVitoria(jogador)
	w.WriteHeader(http.StatusAccepted)
}
```

Mesmo com os testes passando, não temos código funcionando de forma ideal. Se executar a `main` e usar o programa como planejado, não vai funcionar porque ainda não nos dedicamos a implementar corretamente `ArmazenamentoJogador`. Mas isso não é um problema; como focamos no tratamento da requisição, identificamos a interface necessária, ao invés de tentar definir antecipadamente.

*Poderíamos* começar a escrever alguns testes para a `ArmazenamentoJogadorEmMemoria`, mas ela é apenas uma solução temporária até a implementação de um modo mais robusto de registrar as pontuações (por exemplo, em um banco de dados).

O que vamos fazer agora é escrever um *teste de integração* entre `ServidorJogador` e `ArmazenamentoJogadorEmMemoria` para terminar a funcionalidade. Isso vai permitir confiar que a aplicação está funcionando, sem ter que testar diretamente `ArmazenamentoJogadorEmMemoria`. E não apenas isso, mas quando implementarmos `ArmazenamentoJogador` com um banco de dados, usaremos esse mesmo teste para verificar se a implementação funciona como esperado.

### Testes de integração

Testes de integração podem ser úteis para testar partes maiores do sistema, mas saiba que:

* São mais difíceis de escrever
* Quando falham, é difícil saber o porquê (normalmente é um problema dentro de um componente do teste de integração) e pode ser difícil de corrigir
* Às vezes são mais lentos para rodar (porque são usados com componentes "reais", como um banco de dados)

Por isso, é recomendado que pesquise sobre *Pirâmide de Testes*.

## Escreva os testes primeiro

Para ser mais breve, vou te mostrar o teste de integração, já refatorado.

```go
func TestRegistrarVitoriasEBuscarEstasVitorias(t *testing.T) {
	armazenamento := NovoArmazenamentoJogadorEmMemoria()
	servidor := ServidorJogador{armazenamento}
	jogador := "Maria"

	servidor.ServeHTTP(httptest.NewRecorder(), novaRequisicaoRegistrarVitoriaPost(jogador))
	servidor.ServeHTTP(httptest.NewRecorder(), novaRequisicaoRegistrarVitoriaPost(jogador))
	servidor.ServeHTTP(httptest.NewRecorder(), novaRequisicaoRegistrarVitoriaPost(jogador))

	resposta := httptest.NewRecorder()
	servidor.ServeHTTP(resposta, novaRequisicaoObterPontuacao(jogador))
	verificarRespostaCodigoStatus(t, resposta.Code, http.StatusOK)

	verificarCorpoRequisicao(t, resposta.Body.String(), "3")
}
```

* Estamos criando os dois componentes que queremos integrar: `ArmazenamentoJogadorEmMemoria` e `ServidorJogador`.
* Então fazemos 3 requisições para registrar 3 vitórias para `jogador`. Não nos preocupamos com os códigos de retorno no teste, porque isso não é relevante para verificar se a integração funciona como esperado.
* Registramos a próxima resposta (por isso guardamos o valor em `resposta`) porque vamos obter a pontuação do `jogador`.

## Tente rodar o teste

```
--- FAIL: TestRegistrarVitoriasEBuscarEstasVitorias (0.00s)
    servidor_test.go:109: corpo da requisição é inválido, recebido '123' esperado '3'
```

## Escreva código suficiente para passar

Abaixo, há mais código do que o esperado para se escrever sem ter os testes correspondentes.

*Isso é permitido*! Ainda existem testes verificando se as coisas estão funcionando como esperado, mas não focando na parte específica em que estamos trabalhando (`ArmazenamentoJogadorEmMemoria`).

Se houvesse algum problema para continuarmos, era só reverter as alterações para antes do teste que falhou e então escrever mais testes unitários específicos para `ArmazenamentoJogadorEmMemoria`, que nos ajudariam a encontrar a solução.

```go
func NovoArmazenamentoJogadorEmMemoria() *ArmazenamentoJogadorEmMemoria {
	return &ArmazenamentoJogadorEmMemoria{map[string]int{}}
}

type ArmazenamentoJogadorEmMemoria struct {
	armazenamento map[string]int
}

func (s *ArmazenamentoJogadorEmMemoria) RegistrarVitoria(nome string) {
	ja.armazenamento[nome]++
}

func (s *ArmazenamentoJogadorEmMemoria) ObterPontuacaoJogador(nome string) int {
	return ja.armazenamento[nome]
}
```

* Para armazenar os dados, adicionamos um `map[string]int` na struct `ArmazenamentoJogadorEmMemoria`
* Para ajudar nos testes, criamos a `NewArmazenamentoJogadorEmMemoria` para inicializar o armazenamento, e o código do teste de integração foi atualizado para usar esta função (`armazenamento := NewNovoArmazenamentoJogadorEmMemoria()`).
* O resto do código é apenas para fazer o `map` funcionar.

Nosso teste de integração passa, e agora só é preciso mudar o `main` para usar o `NewNovoArmazenamentoJogadorEmMemoria()`

```go
package main

import (
    "log"
    "net/http"
)

func main() {
    servidor := &ServidorJogador{NovoArmazenamentoJogadorEmMemoria()}

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("não foi possível escutar na porta 5000 %v", err)
    }
}
```

Após compilar e rodar, use o `curl` para testar.

* Execute o comando a seguir algumas vezes, mude o nome do jogador se quiser `curl -X POST http://localhost:5000/jogadores/Maria`
* Verifique a pontuação, rodando `curl http://localhost:5000/jogadores/Maria`

Ótimo! Criamos um serviço de acordo com os padrões REST! Se quiser continuar, você pode escolher um armazenamento de dados com maior persistência, que não vai perder os dados quando o programa terminar.

* Escolher uma tecnologia de armazenamento (Bolt? Mongo? Postgres? Sistema de arquivos?)
* Fazer `PostgresArmazenamentoJogador` implementar `ArmazenamentoJogador`
* Desenvolver a funcionalidade usando Desenvolvimento Orientado a Testes para ter certeza de que funciona
* Conectar nos testes de integração, verificar se tudo funciona
* E, finalmente, integrar dentro de `main`.

## Finalizando

### `http.Handler`

* Implemente essa interface para criar servidores web
* Use `http.HandlerFunc` para transformar funções simples em `http.Handler`s
* Use `httptest.NewRecorder` para informar um `ResponseWriter` que permite inspecionar as respostas que a função tratadora envia
* Use `http.NewRequest` para construir as requisições que você espera que seu sistema receba

### Interfaces, Valores predefinidos (*Mocking*) e Injeção de Dependência

* Permitem que você construa a sua aplicação de forma iterativa, um pedaço de cada vez
* Te permite desenvolver uma funcionalidade de tratamento de requisições que precisa de um armazenamento sem precisar exatamente de uma estrutura de armazenamento
* O Desenvolvimento Orientado a Testes nos ajudou a definir as interfaces necessárias

### Cometa pecados, e daí refatore (e então registre no controle de versão)

* Você precisa tratar falhas na compilação ou nos testes como uma situação urgente, a qual precisa resolver o mais rápido possível
* Escreva apenas o código necessário para resolver o problema. *Logo depois* refatore e faça um código melhor
* Ao tentar fazer muitas alterações enquanto o código não está compilando ou os testes estão falhando, corremos o risco de acumular e agravar os problemas
* Nos manter fiéis à essa abordagem nos obriga a escrever pequenos testes, o que significa pequenas alterações, o que nos ajuda a continuar trabalhando em sistemas complexos de forma gerenciável


# JSON, roteamento e aninhamento

[**Você pode encontrar todo o código para este capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/criando-uma-aplicacao/json)

[No capítulo anterior](/aprenda-go-com-testes/main/criando-uma-aplicacao/servidor-http) nós criamos um servidor web para armazenar quantos jogos nossos jogadores venceram.

Nossa gerente de produtos veio com um novo requisito; criar um novo endpoint chamado `/liga` que retorne uma lista contendo todos os jogadores armazenados. Ela gostaria que isto fosse retornado como um JSON.

## Este é o código que temos até agora

```go
// servidor.go
package main

import (
    "fmt"
    "net/http"
)

type ArmazenamentoJogador interface {
    ObtemPontuacaoDoJogador(nome string) int
    GravarVitoria(nome string)
}

type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
}

func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    switch r.Method {
    case http.MethodPost:
        s.processarVitoria(w, jogador)
    case http.MethodGet:
        s.mostrarPontuacao(w, jogador)
    }
}

func (s *ServidorJogador) mostrarPontuacao(w http.ResponseWriter, jogador string) {
    pontuação := s.armazenamento.ObtemPontuacaoDoJogador(jogador)

    if pontuação == 0 {
        w.WriteHeader(http.StatusNotFound)
    }

    fmt.Fprint(w, pontuação)
}

func (s *ServidorJogador) processarVitoria(w http.ResponseWriter, jogador string) {
    s.armazenamento.GravarVitoria(jogador)
    w.WriteHeader(http.StatusAccepted)
}
```

```go
// ArmazenamentoDeJogadorNaMemoria.go
package main

func NovoArmazenamentoDeJogadorNaMemoria() *ArmazenamentoDeJogadorNaMemoria {
    return &ArmazenamentoDeJogadorNaMemoria{map[string]int{}}
}

type ArmazenamentoDeJogadorNaMemoria struct {
    armazenamento map[string]int
}

func (a *ArmazenamentoDeJogadorNaMemoria) GravarVitoria(nome string) {
    a.armazenamento[nome]++
}

func (a *ArmazenamentoDeJogadorNaMemoria) ObtemPontuacaoDoJogador(nome string) int {
    return a.armazenamento[nome]
}
```

```go
// main.go
package main

import (
    "log"
    "net/http"
)

func main() {
    servidor := &ServidorJogador{NovoArmazenamentoDeJogadorNaMemoria()}

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("não foi possível ouvir na porta 5000 %v", err)
    }
}
```

Você pode encontrar os testes correspondentes no endereço no topo do capítulo.

Nós vamos começar criando o endpoint para a tabela de `liga`.

## Escreva os testes primeiro

Ampliaremos a suite de testes existente, pois temos algumas funções de teste úteis e um `ArmazenamentoJogador` falso para usar.

```go
// server_test.go

func TestLiga(t *testing.T) {
    armazenamento := EsbocoArmazenamentoJogador{}
    servidor := &ServidorJogador{&armazenamento}

    t.Run("retorna 200 em /liga", func(t *testing.T) {
        requisicao, _ := http.NewRequest(http.MethodGet, "/liga", nil)
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificaStatus(t, resposta.Code, http.StatusOK)
    })
}
```

Antes de nos preocuparmos sobre as pontuações atuais e o JSON, nós vamos tentar manter as mudanças pequenas com o plano de ir passo a passo rumo ao nosso objetivo. O início mais simples é checar se nós conseguimos consultar `/liga` e obter um `OK` de retorno.

## Tente rodar os testes

```
=== RUN   TestLiga/retorna_200_em_/liga
panic: runtime error: slice bounds out of range [recovered]
    panic: runtime error: slice bounds out of range

goroutine 6 [running]:
testing.tRunner.func1(0xc42010c3c0)
    /usr/local/Cellar/go/1.10/libexec/src/testing/testing.go:742 +0x29d
panic(0x1274d60, 0x1438240)
    /usr/local/Cellar/go/1.10/libexec/src/runtime/panic.go:505 +0x229
github.com/larien/aprenda-go-com-testes/json-and-io/v2.(*ServidorJogador).ServeHTTP(0xc420048d30, 0x12fc1c0, 0xc420010940, 0xc420116000)
    /Users/larien/go/src/github.com/larien/aprenda-go-com-testes/json-and-io/v2/servidor.go:20 +0xec
```

Seu `ServidorJogador` deve estar sendo abortado por um panic como acima. Vá para a linha de código que está apontando para `servidor.go` no stack trace.

```go
jogador := r.URL.Path[len("/jogadores/"):]
```

No capítulo anterior, nós mencionamos que esta era uma maneira bastante ingênua de fazer o nosso roteamento. O que está acontecendo é que ele está tentando cortar a string do caminho da URL começando do índice após `/liga` e então, isto nos dá um `slice bounds out of range`.

## Escreva somente o código suficiente para fazê-lo passar

Go tem um mecanismo de rotas nativo (built-in) chamado [`ServeMux`](https://golang.org/pkg/net/http/#ServeMux) (requisição multiplexadora) que nos permite atracar um `http.Handler` para caminhos de uma requisição em específico.

Vamos cometer alguns pecados e obter os testes passando da maneira mais rápida que pudermos, sabendo que nós podemos refatorar isto com segurança uma vez que nós soubermos que os testes estão passando.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {

    roteador := http.NewServeMux()

    roteador.Handle("/liga", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    }))

    roteador.Handle("/jogadores/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        jogador := r.URL.Path[len("/jogadores/"):]

        switch r.Method {
        case http.MethodPost:
            s.processarVitoria(w, jogador)
        case http.MethodGet:
            s.mostrarPontuacao(w, jogador)
        }
    }))

    roteador.ServeHTTP(w, r)
}
```

* Quando a requisição começa nós criamos um roteador e então dizemos para o caminho `x` usar o handler `y`.
* Então para nosso novo endpoint, nós usamos `http.HandlerFunc` e uma *função anônima* para `w.WriteHeader(http.StatusOK)` quando `/liga` é requisitada para fazer nosso novo teste passar.
* Para a rota `/jogadores/` nós somente recortamos e colamos nosso código dentro de outro `http.HandlerFunc`.
* Finalmente, nós lidamos com a requisição que está vindo chamando nosso novo roteador `ServeHTTP` (notou como `ServeMux` é *também* um `http.Handler`?)

## Refatorando

`ServeHTTP` parece um pouco grande, nós podemos separar as coisas um pouco refatorando nossos handlers em métodos separados.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(s.manipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(s.manipulaJogadores))

    roteador.ServeHTTP(w, r)
}

func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
}

func (s *ServidorJogador) manipulaJogadores(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    switch r.Method {
    case http.MethodPost:
        s.processarVitoria(w, jogador)
    case http.MethodGet:
        s.mostrarPontuacao(w, jogador)
    }
}
```

É um pouco estranho (e ineficiente) estar configurando um roteador quando uma requisição chegar e então chamá-lo. O que idealmente queremos fazer é uma função do tipo `NovoServidorJogador` que pegará nossas dependências e ao ser chamada, irá fazer a configuração única da criação do roteador. Desta forma, cada requisição pode usar somente uma instância do nosso roteador.

```go
type ServidorJogador struct {
    armazenamento  ArmazenamentoJogador
    roteador *http.ServeMux
}

func NovoServidorJogador(armazenamento ArmazenamentoJogador) *ServidorJogador {
    p := &ServidorJogador{
        armazenamento,
        http.NewServeMux(),
    }

    s.roteador.Handle("/liga", http.HandlerFunc(s.manipulaLiga))
    s.roteador.Handle("/jogadores/", http.HandlerFunc(s.manipulaJogadores))

    return s
}

func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s.roteador.ServeHTTP(w, r)
}
```

* `ServidorJogador` agora precisa armazenar um roteador.
* Nós movemos a criação do roteador para fora de `ServeHTTP` e colocamos dentro do nosso `NovoServidorJogador`, então isto só será feito uma vez, não por requisição.
* Você vai precisar atualizar todos os testes e código de produção onde nós costumávamos fazer `ServidorJogador{&armazenamento}` por `NovoServidorJogador(&armazenamento)`.

### Uma refatoração final

Tente mudar o código para o seguinte:

```go
type ServidorJogador struct {
    armazenamento  ArmazenamentoJogador
    http.Handler
}

func NovoServidorJogador(armazenamento ArmazenamentoJogador) *ServidorJogador {
    s := new(ServidorJogador)

    s.armazenamento = armazenamento

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(s.manipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(s.manipulaJogadores))

    s.Handler = roteador

    return s
}
```

Finalmente, se certifique de que você **deletou** `func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request)` por não ser mais necessária!

## Incorporando

Nós mudamos a segunda propriedade de `ServidorJogador` removendo a propriedade nomeada `roteador http.ServeMux` e substituindo por `http.Handler`; isto é chamado de *incorporar*.

> O Go não provê a noção típica de subclasses orientada por tipo, mas tem a habilidade de "emprestar" partes de uma implementação por incorporar tipos dentro de uma struct ou interface.

[Effective Go - Embedding](https://golang.org/doc/effective_go.html#embedding)

O que isto quer dizer é que nosso `ServidorJogador` agora tem todos os métodos que `http.Handler` têm, que é somente o `ServeHTTP`.

Para "preencher" o `http.Handler` nós atribuímos ele para o `roteador` que nós criamos em `NovoServidorJogador`. Nós podemos fazer isso porque `http.ServeMux` tem o método `ServeHTTP`.

Isto nos permite remover nosso próprio método `ServeHTTP`, pois nós já estamos expondo um via o tipo incorporado.

Incorporamento é um recurso muito interessante da linguagem. Você pode usar isto com interfaces para compor novas interfaces.

```go
type Animal interface {
    Comedor
    Dormente
}
```

E você pode usar isto com tipos concretos também, não somente interfaces. Como você pode esperar, se você incorporar um tipo concreto você vai ter acesso a todos os seus métodos e campos públicos.

### Alguma desvantagem?

Você deve ter cuidado ao incorporar tipos porque você vai expor todos os métodos e campos públicos do tipo que você incorporou. Em nosso caso, está tudo bem porque nós haviamos incorporado apenas a *interface* que nós queremos expôr (`http.Handler`).

Se nós tivéssemos sido "preguiçosos" e incorporado `http.ServeMux` (o tipo concreto) por exemplo, também funcionaria *porém* os usuários de `ServidorJogador` seriam capazes de adicionar novas rotas ao nosso servidor porque o método `Handle(path, handler)` seria público.

**Quando incorporamos tipos, realmente devemos pensar sobre qual o impacto que isto terá em nossa API pública**

Isto é um erro *muito* comum de mau uso de incorporamento, que termina poluindo nossas APIs e expondo os métodos internos dos seus tipos incorporados.

Agora que nós reestruturamos nossa aplicação, nós podemos facilmente adicionar novas rotas e botar para funcionar nosso endpoint `/liga`. Agora precisamos fazê-lo retornar algumas informações úteis.

Nós devemos retornar um JSON semelhante a este:

```javascript
[
   {
      "Nome":"Bill",
      "Vitórias":10
   },
   {
      "Nome":"Alice",
      "Vitórias":15
   }
]
```

## Escreva o teste primeiro

Nós vamos começar tentando analizar a resposta dentro de algo mais significativo.

```go
func TestLiga(t *testing.T) {
    armazenamento := EsbocoArmazenamentoJogador{}
    servidor := NovoServidorJogador(&armazenamento)

    t.Run("retorna 200 em /liga", func(t *testing.T) {
        requisicao, _ := http.NewRequest(http.MethodGet, "/liga", nil)
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        var obtido []Jogador

        err := json.NewDecoder(resposta.Body).Decode(&obtido)

        if err != nil {
            t.Fatalf ("Não foi possível fazer parse da resposta do servidor '%s' no slice de Jogador, '%v'", resposta.Body, err)
        }

        verificaStatus(t, resposta.Code, http.StatusOK)
    })
}
```

### Por que não testar o JSON como texto puro?

Você pode argumentar que um simples teste inicial poderia só comparar que o não foi possível ouvir na porta 5000 tem um particular texto em JSON.

Na minha experiência, testes que comparam JSONs de forma literal possuem os seguintes problemas:

* *Fragilidade*. Se você mudar o modelo dos dados seu teste irá falhar.
* *Difícil de debugar*. Pode ser complicado de entender qual é o problema real ao se comparar dois textos JSON.
* *Má intenção*. Embora a saída deva ser JSON, o que é realmente importante é exatamente o que o dado é, ao invés de como ele está codificado.
* *Re-testando a biblioteca padrão*. Não há a necessidade de testar como a biblioteca padrão gera JSON, ela já está testada. Não teste o código de outras pessoas.

Ao invés disso, nós poderíamos analisar o JSON dentro de estruturas de dados que são relevantes para nós e nossos testes.

### Modelagem de dados

Dado o modelo de dados do JSON, parece que nós precisamos de uma lista de `Jogador` com alguns campos, sendo assim nós criaremos um novo tipo para capturarmos isso.

```go
type Jogador struct {
    Nome string
    Vitorias int
}
```

### Decodificação de JSON

```go
var obtido []Jogador
err := json.NewDecoder(resposta.Body).Decode(&obtido)
```

Para analizar o JSON dentro de nosso modelo de dados nós criamos um `Decoder` do pacote `encoding/json` e então chamamos seu método `Decode`. Para criar um `Decoder` é necessário ler de um `io.Reader`, que em nosso caso é nossa própria resposta `Body`.

`Decode` pega o endereço da coisa que nós estamos tentando decodificar, e é por isso que nós declaramos um slice vazio de `Jogador` na linha anterior.

Esse processo de analisar um JSON pode falhar, então `Decode` pode retornar um `error`. Não há ponto de continuidade para o teste se isto acontecer, então nós checamos o erro e paramos o teste com `t.Fatalf`. Note que nós exibimos o não foi possível ouvir na porta 5000 junto do erro, pois é importante para qualquer outra pessoa que esteja rodando os testes ver que o texto não pôde ser analisado.

## Tente rodar o teste

```
=== RUN   TestLiga/retorna_200_em_/liga
    --- FAIL: TestLiga/retorna_200_em_/liga (0.00s)
        server_test.go:107: Não foi possível fazer parse da resposta do servidor '' no slice de Jogador, 'unexpected end of JSON input'
```

Nosso endpoint atualmente não retorna um corpo, então isso não pode ser analisado como JSON.

## Escreva código suficiente para fazê-lo passar

```go
func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    tabelaDaLiga := []Jogador{
        {"Chris", 20},
    }

    json.NewEncoder(w).Encode(tabelaDaLiga)

    w.WriteHeader(http.StatusOK)
}
```

Os testes agora passam.

### Codificando e decodificando

Note a amável simetria na biblioteca padrão.

* Para criar um `Encoder` você precisa de um `io.Writer` que é o que `http.ResponseWriter` implementa.
* Para criar um `Decoder` você precisa de um `io.Reader` que o campo `Body` da nossa resposta implementa.

Ao longo deste livro, nós temos usado `io.Writer`. Isso é uma outra demonstração desta prevalência nas bibliotecas padrões e de como várias bibliotecas facilmente trabalham em conjunto com elas.

## Refatoração

Seria legal introduzir uma separação de conceitos entre nosso handler e o trecho de obter o `tabelaDaLiga`. Como sabemos, nós não vamos codificar isso por agora.

```go
func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(s.obterTabelaDaLiga())
    w.WriteHeader(http.StatusOK)
}

func (s *ServidorJogador) obterTabelaDaLiga() []Jogador{
    return []Jogador{
        {"Chris", 20},
    }
}
```

Mais adiante, nós vamos querer estender nossos testes para então podermos controlar exatamente qual dado nós queremos receber de volta.

## Escreva o teste primeiro

Nós podemos atualizar o teste para afirmar que a tabela das ligas contem alguns jogadores que nós vamos pôr em nossa loja.

Atualize `EsbocoArmazenamentoJogador` para permitir que ele armazene uma liga, que é apenas um slice de `Jogador`. Nós vamos armazenar nossos dados esperados lá.

```go
type EsbocoArmazenamentoJogador struct {
    pontuações   map[string]int
    chamadasDeVitoria []string
    liga []Jogador
}
```

Adiante, atualize nossos testes colocando alguns jogadores na propriedade da liga, para então afirmar que eles foram retornados do nosso servidor.

```go
func TestLiga(t *testing.T) {

    t.Run("retorna a tabela da Liga como JSON", func(t *testing.T) {
        ligaEsperada := []Jogador{
            {"Cleo", 32},
            {"Chris", 20},
            {"Tiest", 14},
        }

        armazenamento := EsbocoArmazenamentoJogador{nil, nil, ligaEsperada}
        servidor := NovoServidorJogador(&armazenamento)

        requisicao, _ := http.NewRequest(http.MethodGet, "/liga", nil)
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        var obtido []Jogador

        err := json.NewDecoder(resposta.Body).Decode(&obtido)

        if err != nil {
            t.Fatalf("Não foi possível fazer parse da resposta do servidor '%s' no slice de Jogador, '%v'", resposta.Body, err)
        }

        verificaStatus(t, resposta.Code, http.StatusOK)

        if !reflect.DeepEqual(obtido, ligaEsperada) {
            t.Errorf("obtido %v esperado %v", obtido, ligaEsperada)
        }
    })
}
```

## Tente rodar o teste

```
./server_test.go:33:3: too few values in struct initializer
./server_test.go:70:3: too few values in struct initializer
```

## Escreva o minimo de código para que o teste rode e cheque as falhas na saída dele.

Você vai precisar atualizar os outros testes, assim como nós temos um novo campo em `EsbocoArmazenamentoJogador`; ponha-o como nulo para os outros testes.

Tente executar os testes novamente e você deverá ter:

```
=== RUN   TestLiga/retorna_a_tabela_da_liga_como_JSON
    --- FAIL: TestLiga/retorna_a_tabela_da_liga_como_JSON (0.00s)
        server_test.go:124: obtido [{Chris 20}] esperado [{Cleo 32} {Chris 20} {Tiest 14}]
```

## Escreva código suficiente para fazê-lo passar

Nós sabemos que o dado está em nosso `EsbocoArmazenamentoJogador` e nós abstraímos esses dados para uma interface `ArmazenamentoJogador`. Nós precisamos atualizar isto então qualquer um passando-nos um `ArmazenamentoJogador` pode prover-nos com dados para as ligas.

```go
type ArmazenamentoJogador interface {
    ObtemPontuacaoDoJogador(nome string) int
    GravarVitoria(nome string)
    ObterLiga() []Jogador
}
```

Agora nós podemos atualizar o código do nosso handler para chamar isto ao invés de retornar uma lista manualmente escrita. Delete nosso método `obterTabelaDaLiga()` e então atualize `manipulaLiga` para chamar `ObterLiga()`.

```go
func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(s.armazenamento.ObterLiga())
    w.WriteHeader(http.StatusOK)
}
```

Tente executar os testes:

```
# github.com/larien/aprenda-go-com-testes/json-and-io/v4
./main.go:9:50: cannot use NovoArmazenamentoDeJogadorNaMemoria() (type *ArmazenamentoDeJogadorNaMemoria) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *ArmazenamentoDeJogadorNaMemoria does not implement ArmazenamentoJogador (missing ObterLiga method)
./servidor_integration_test.go:11:27: cannot use armazenamento (type *ArmazenamentoDeJogadorNaMemoria) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *ArmazenamentoDeJogadorNaMemoria does not implement ArmazenamentoJogador (missing ObterLiga method)
./server_test.go:36:28: cannot use &armazenamento (type *EsbocoArmazenamentoJogador) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *EsbocoArmazenamentoJogador does not implement ArmazenamentoJogador (missing ObterLiga method)
./server_test.go:74:28: cannot use &armazenamento (type *EsbocoArmazenamentoJogador) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *EsbocoArmazenamentoJogador does not implement ArmazenamentoJogador (missing ObterLiga method)
./server_test.go:106:29: cannot use &armazenamento (type *EsbocoArmazenamentoJogador) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *EsbocoArmazenamentoJogador does not implement ArmazenamentoJogador (missing ObterLiga method)
```

O compilador está reclamando porque `ArmazenamentoDeJogadorNaMemoria` e `EsbocoArmazenamentoJogador` não tem os novos métodos que nós adicionamos em nossa interface.

Para `EsbocoArmazenamentoJogador` isto é bem fácil, apenas retorne o campo `liga` que nós adicionamos anteriormente.

```go
func (s *EsbocoArmazenamentoJogador) ObterLiga() []Jogador {
    return s.liga
}
```

Aqui está uma lembrança de como `InMemoryStore` é implementado:

```go
type ArmazenamentoDeJogadorNaMemoria struct {
    armazenamento map[string]int
}
```

Embora seja bastante simples para implementar `ObterLiga` "propriamente", iterando sobre o map, lembre que nós estamos apenas tentando *escrever o mínimo de código para fazer os testes passarem*.

Então vamos apenas deixar o compilador feliz por enquanto e viver com o desconfortável sentimento de uma implementação incompleta em nosso `InMemoryStore`.

```go
func (a *ArmazenamentoDeJogadorNaMemoria) ObterLiga() []Jogador {
    return nil
}
```

O que isto está realmente nos dizendo é que *depois* nós vamos querer testar isto, porém vamos estacionar isto por hora.

Tente executar os testes, o compilador deve passar e os testes deverão estar passando!

## Refatoração

O código de teste não transmite suas intenções muito bem e possui vários trechos que podem ser refatorados.

```go
t.Run("retorna a tabela da Liga como JSON", func(t *testing.T) {
    ligaEsperada := []Jogador{
        {"Cleo", 32},
        {"Chris", 20},
        {"Tiest", 14},
    }

    armazenamento := EsbocoArmazenamentoJogador{nil, nil, ligaEsperada}
    servidor := NovoServidorJogador(&armazenamento)

    requisicao := novaRequisicaoDeLiga()
    resposta := httptest.NewRecorder()

    servidor.ServeHTTP(resposta, requisicao)

    obtido := obterLigaDaResposta(t, resposta.Body)
    verificaStatus(t, resposta.Code, http.StatusOK)
    verificaLiga(t, obtido, ligaEsperada)
})
```

Aqui estão os novos helpers:

```go
func obterLigaDaResposta(t *testing.T, body io.Reader) (liga []Jogador) {
    t.Helper()
    err := json.NewDecoder(body).Decode(&liga)

    if err != nil {
        t.Fatalf("Não foi possível fazer parse da resposta do servidor '%s' no slice de Jogador, '%v'", body, err)
    }

    return
}

func verificaLiga(t *testing.T, obtido, esperado []Jogador) {
    t.Helper()
    if !reflect.DeepEqual(obtido, esperado) {
        t.Errorf("obtido %v esperado %v", obtido, esperado)
    }
}

func novaRequisicaoDeLiga() *http.Request {
    req, _ := http.NewRequest(http.MethodGet, "/liga", nil)
    return req
}
```

Uma última coisa que nós precisamos fazer para nosso servidor funcionar é ter certeza de que nós retornamos um `content-type` correto na resposta, então as máquinas podem reconhecer que nós estamos retornando um `JSON`.

## Escreva os testes primeiro

Adicione essa afirmação no teste existente

```go
if resposta.Result().Header.Get("content-type") != "application/json" {
    t.Errorf("resposta não tinha o tipo de conteúdo de application/json, obtido %v", resposta.Result().Header)
}
```

## Tente rodar o teste

```
=== RUN   TestLiga/retorna_a_tabela_da_liga_como_JSON
    --- FAIL: TestLiga/retorna_a_tabela_da_liga_como_JSON (0.00s)
        server_test.go:124: resposta não tinha o tipo de conteúdo de application/json, obtido map[Content-Type:[text/plain; charset=utf-8]]
```

## Escreva código suficiente para fazê-lo passar

Atualize `manipulaLiga`

```go
func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("content-type", "application/json")
    json.NewEncoder(w).Encode(s.armazenamento.ObterLiga())
}
```

O teste deve passar.

## Refatoração

Adicione um helper para `verificaTipoDoConteudo`.

```go
const tipoDoConteudoJSON = "application/json"

func verificaTipoDoConteudo(t *testing.T, resposta *httptest.ResponseRecorder, esperado string) {
    t.Helper()
    if resposta.Result().Header.Get("content-type") != esperado {
        t.Errorf("resposta não obteve content-type de %s, obtido %v", esperado, resposta.Result().Header)
    }
}
```

Use isso no teste.

```go
verificaTipoDoConteudo(t, resposta, tipoDoConteudoJSON)
```

Agora que nós resolvemos `ServidorJogador`, por agora podemos mudar nossa atenção para `ArmazenamentoDeJogadorNaMemoria` porque no momento se nós tentarmos demonstrá-lo para o gerente de produto, `/liga` não vai funcionar.

A forma mais rápida de nós termos alguma confiança é adicionar a nosso teste de integração, nós podemos bater no novo endpoint e checar se nós recebemos a resposta correta de `/liga`.

## Escreva o teste primeiro

Nós podemos usar `t.Run` para parar este teste um pouco e então reusar os helpers dos testes do nosso servidor - novamente mostrando a importância de refatoração dos testes.

```go
func TestGravaVitoriasEAsRetorna(t *testing.T) {
    armazenamento := NovoArmazenamentoDeJogadorNaMemoria()
    servidor := NovoServidorJogador(armazenamento)
    jogador := "Pepper"

    servidor.ServeHTTP(httptest.NewRecorder(), novaRequisiçãoPostDeVitoria(jogador))
    servidor.ServeHTTP(httptest.NewRecorder(), novaRequisiçãoPostDeVitoria(jogador))
    servidor.ServeHTTP(httptest.NewRecorder(), novaRequisiçãoPostDeVitoria(jogador))

    t.Run("obter pontuação", func(t *testing.T) {
        resposta := httptest.NewRecorder()
        servidor.ServeHTTP(resposta, novaRequisicaoObterPontuacao(jogador))
        verificaStatus(t, resposta.Code, http.StatusOK)

        verificaCorpoDaResposta(t, resposta.Body.String(), "3")
    })

    t.Run("obter liga", func(t *testing.T) {
        resposta := httptest.NewRecorder()
        servidor.ServeHTTP(resposta, novaRequisicaoDeLiga())
        verificaStatus(t, resposta.Code, http.StatusOK)

        obtido := obterLigaDaResposta(t, resposta.Body)
        esperado := []Jogador{
            {"Pepper", 3},
        }
        verificaLiga(t, obtido, esperado)
    })
}
```

## Tente rodar o teste

```
=== RUN   TestGravaVitoriasEAsRetorna/obter_liga
    --- FAIL: TestGravaVitoriasEAsRetorna/obter_liga (0.00s)
        servidor_integration_test.go:35: obtido [] esperado [{Pepper 3}]
```

## Escreva código suficiente para fazê-lo passar

`ArmazenamentoDeJogadorNaMemoria` is returning `nil` when you call `ObterLiga()` so we'll need to fix that.

```go
func (a *ArmazenamentoDeJogadorNaMemoria) ObterLiga() []Jogador {
    var liga []Jogador
    for nome, vitórias := range a.armazenamento {
        liga = append(liga, Jogador{nome, vitórias})
    }
    return liga
}
```

Tudo que nós precisamos fazer é iterar através do map e converter cada chave/valor para um `Jogador`

O teste deve passar agora.

## Concluindo

Nós temos continuado a seguramente iterar no nosso programa usando TDD, fazendo ele suportar novos endpoints de uma forma manutenível com um roteador e isso pode agora retornar JSON para nossos consumidores. No próximo capítulo, nós vamos cobrir persistência de dados e ordenação de nossas ligas.

O que nós cobrimos:

* **Roteamento**. A biblioteca padrão oferece uma fácil forma de usar tipos para fazer roteamento. Ela abraça completamente a interface `http.Handler` nela, tanto que você pode atribuir rotas para `Handler`s e a rota em si também é um `Handler`. Ela não tem alguns recursos que você pode esperar, como caminhos para variáveis (ex. `/users/{id}`). Você pode facilmente analisar esta informação por si mesmo porém você pode querer considerar olhar para outras bibliotecas de roteamento se isso se tornar um fardo. Muitas das mais populares seguem a filosofia das bibliotecas padrões e também implementam `http.Handler`.
* **Composição**. Nós tocamos um pouco nesta técnica porém você pode [ler mais sobre isso de Effective Go](https://golang.org/doc/effective_go.html#embedding). Se há uma coisa que você deve tirar disso é que composições podem ser extremamente úteis, porém *sempre pensando na sua API pública, só exponha o que é apropriado*.
* **Serialização e Desserialização de JSON**. A biblioteca padrão faz isto de forma bastante trivial ao serializar e desserializar nosso dado. Isto também abre para configurações e você pode customizar como esta transformação de dados funciona se necessário.


# IO e sorting

[**Você pode encontrar todo o código para este capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/criando-uma-aplicacao/io)

[No capitulo anterior](https://github.com/larien/learn-go-with-tests/blob/main/criando-uma-aplicacao/json.md) continuamos interagindo com nossa aplicação pela adição de um novo endpoint `/liga`. Durante o caminho aprendemos como lidar com JSON, tipos embutidos e roteamento.

Nossa dona do produto está de certa forma preocupada, por conta do software perder as pontuações quando o servidor é reiniciado. Ela também não se agradou que nós não interpretamos o endpoint `/liga` que deveria retornar os jogadores ordenados pelo número de vitórias!

## O código até agora

```go
// server.go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

// GuardaJogador armazena informações sobre os jogadores
type GuardaJogador interface {
    PegaPontuacaoDoJogador(nome string) int
    SalvaVitoria(nome string)
    PegaLiga() []Jogador
}

// Jogador guarda o nome com o número de vitorias
type Jogador struct {
    Nome string
    Vitorias int
}

// ServidorDoJogador é uma interface HTTP para informações dos jogadores
type ServidorDoJogador struct {
    armazenamento GuardaJogador
    http.Handler
}

const jsonContentType = "application/json"

// NovoServidorDoJogador cria um ServidorDoJogador com roteamento configurado
func NovoServidorDoJogador(armazenamento GuardaJogador) *ServidorDoJogador {
    p := new( ServidorDoJogador)

    p.armazenamento = armazenamento

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(p.ManipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(p.ManipulaJogador))

    p.Handler = roteador

    return p
}

func (p *ServidorDoJogador) ManipulaLiga(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(p.armazenamento.PegaLiga())
    w.Header().Set("content-type", jsonContentType)
    w.WriteHeader(http.StatusOK)
}

func (p *ServidorDoJogador) ManipulaJogador(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    switch r.Method {
    case http.MethodPost:
        p.processaVitoria(w, jogador)
    case http.MethodGet:
        p.mostraPontuacao(w, jogador)
    }
}

func (p *ServidorDoJogador) mostraPontuacao(w http.ResponseWriter, jogador string) {
    pontuacao := p.armazenamento.PegaPontuacaoDoJogador(jogador)

    if pontuacao == 0 {
        w.WriteHeader(http.StatusNotFound)
    }

    fmt.Fprint(w, pontuacao)
}

func (p *ServidorDoJogador) processaVitoria(w http.ResponseWriter, jogador string) {
    p.armazenamento.salvaVitorias(jogador)
    w.WriteHeader(http.StatusAccepted)
}
```

```go
// ArmazenamentoDeJogadorNaMemoria.go
package main

func NovoArmazenamentoDeJogadorNaMemoria() *ArmazenamentoDeJogadorNaMemoria {
    return &ArmazenamentoDeJogadorNaMemoria{map[string]int{}}
}

type ArmazenamentoDeJogadorNaMemoria struct {
    armazenamento map[string]int
}

func (i *ArmazenamentoDeJogadorNaMemoria) PegaLiga() []Jogador {
    var liga []Jogador
    for nome, vitorias := range i.armazenamento {
        liga = append(liga, Jogador{nome, vitorias})
    }
    return liga
}

func (i *ArmazenamentoDeJogadorNaMemoria) SalvaVitoria(nome string) {
    i.armazenamento[nome]++
}

func (i *ArmazenamentoDeJogadorNaMemoria) PegaPontuacaoDoJogador(nome string) int {
    return i.armazenamento[nome]
}
```

```go
// main.go
package main

import (
    "log"
    "net/http"
)

func main() {
    servidor:= NovoServidorDoJogador(NovoArmazenamentoDeJogadorNaMemoria())

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("Não foi possivel ouvir na porta 5000 %v", err)
    }
}
```

Você pode encontrar todos os testes relacionados no link no começo desse capítulo.

## Armazene os dados

Existem diversos bancos de dados que poderíamos usar para isso, mas nós vamos por uma abordagem mais simples. Nós iremos armazenar os dados para essa aplicação em um arquivo como JSON.

Isso mantém os dados bastante manipuláveis e é relativamente simples de implementar.

Não será bem escalável mas, dado que isto é um protótipo, vai funcionar para agora. Se nossas circunstâncias mudarem e isto não for mais apropriado, será simples trocar para algo diferente por conta da abstração de `GuardarJogadores` que nós usamos.

Nós vamos manter o `NovoArmazenamentoDeJogadorNaMemoria` por enquanto para que os testes de integração continuem passando a medida que formos desenvolvendo nossa armazenamento. Quando estivermos confiantes que nossa implementação é suficiente para fazer os testes de integração passarem , nós iremos trocar e apagar `NovoArmazenamentoDeJogadorNaMemoria`

## Escreva os testes primeiro

Por agora você deve estar familiar com as interfaces em torno da biblioteca padrão para leitura de dados (`io.Reader`), escrita de dados (`io.Writer`) e como nós podemos usar a biblioteca padrão para testar essas funções sem ter que usar arquivos de verdade.

Para esse trabalho ser completo precisamos implementar `GuardaJogador` , então escreveremos testes para nossa armazenamento chamando os métodos que nós precisamos implementar. Começaremos com `PegaLiga`.

```go
func TestSistemaDeArquivoDeArmazenamentoDoJogador(t *testing.T) {

    t.Run("/liga de um leitor", func(t *testing.T) {
        bancoDeDados := strings.NewReader(`[
            {"Nome": "Cleo", "Vitorias": 10},
            {"Nome": "Chris", "Vitorias": 33}]`)

        armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

        recebido := armazenamento.PegaLiga()

        esperado := []Jogador{
            {"Cleo", 10},
            {"Chris", 33},
        }

        defineLiga(t, recebido, esperado)
    })
}
```

Estamos usando `strings.NewReader` que irá nos retornar um `Reader`, que é o que nosso `SistemaDeArquivoDeArmazenamentoDoJogador` irá usar para ler os dados. Em `main` abriremos um arquivo, que também é um `Reader`.

## Tente rodar o teste

```
# github.com/larien/aprenda-go-com-testes/json-and-io/v7
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:15:12: undefined: SistemaDeArquivoDeArmazenamentoDoJogador
```

## Escreva código suficiente para fazer o teste rodar e veja o retorno do erro do teste

Vamos definir `SistemaDeArquivoDeArmazenamentoDoJogador` em um novo arquivo

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {}
```

Tente de novo

```
# github.com/larien/aprenda-go-com-testes/json-and-io/v7
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:15:28: too many values in struct initializer
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:17:15: armazenamento.PegaLiga undefined (type SistemaDeArquivoDeArmazenamentoDoJogador has no field or method PegaLiga)
```

Está reclamando porque estamos passando para ele um `Reader` mas não está esperando um e não tem `PegaLiga` definida ainda.

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.Reader
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() []Jogador {
    return nil
}
```

Tente mais uma vez...

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador//league_from_a_reader
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador//league_from_a_reader (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:24: recebido [] esperado [{Cleo 10} {Chris 33}]
```

## Escreva código suficiente para fazer passar

Nós lemos JSON de um leitor antes

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() []Jogador {
    var liga []Jogador
    json.NewDecoder(f.bancoDeDados).Decode(&liga)
    return liga
}
```

O teste deve passar.

## Refatore

*Fizemos* isso antes! Nosso código de teste para o servidor tinha que decodificar o JSON da resposta.

Vamos tentar DRYando isso em uma função.

Crie um novo arquivo chamado `liga.go` e coloque isso nele.

```go
func NovaLiga(rdr io.Reader) ([]Jogador, error) {
    var liga []Jogador
    err := json.NewDecoder(rdr).Decode(&liga)
    if err != nil {
        err = fmt.Errorf("Problema parseando a liga, %v", err)
    }

    return liga, err
}
```

Chame isso em nossa implementação e em nosso teste helper `obterLigaDaResposta` in `serv_test.go`

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() []Jogador {
    liga, _ := NovaLiga(f.bancoDeDados)
    return liga
}
```

Ainda não temos a estratégia para lidar com a análise de erros mas vamos continuar.

### Procurando problemas

Existe um problema na nossa implementação. Primeiramente, vamos relembrar como `io.Reader` é definida.

```go
type Reader interface {
    Read(p []byte) (n int, err error)
}
```

Com nosso arquivo, você consegue imagina-lo lendo byte por byte até o fim. O que acontece se você tentar e `ler` uma segunda vez?

Adicione o seguinte no final do seu teste atual.

```go
// read again
recebido = armazenamento.PegaLiga()
defineLiga(t, recebido, esperado)
```

Queremos que passe, mas se você rodar o teste ele não passa.

O problema é nosso `Reader` chegou no final, então não tem mais nada para ser lido. Precisamos de um jeito de avisar para voltar ao inicio.

[ReadSeeker](https://golang.org/pkg/io/#ReadSeeker) é outra interface na biblioteca padrão que pode ajudar.

```go
type ReadSeeker interface {
    Reader
    Seeker
}
```

Lembra-se do incorporamento? Esta é uma interface composta de `Reader` e [`Seeker`](https://golang.org/pkg/io/#Seeker)

```go
type Seeker interface {
    Seek(offset int64, whence int) (int64, error)
}
```

Parece bom, podemos mudar `SistemaDeArquivoDeArmazenamentoDoJogador` para pegar essa interface no lugar?

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.ReadSeeker
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() []Jogador {
    f.bancoDeDados.Seek(0, 0)
    liga, _ := NovaLiga(f.bancoDeDados)
    return liga
}
```

Tente rodar o teste,agora passa! Ainda bem que `string.NewReader` que nós usamos em nosso teste também implementa `ReadSeeker` então não precisamos mudar nada.

A seguir vamos implementar `PegarPontuacaooDoJogador`.

## Escreva o teste primeiro

```go
t.Run("pegar pontuação do jogador", func(t *testing.T) {
    bancoDeDados := strings.NewReader(`[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

    recebido := armazenamento.("Chris")

    esperado := 33

    if recebido != esperado {
        t.Errorf("recebido %d esperado %d", recebido, esperado)
    }
})
```

## Tente rodar o teste

`./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:38:15: armazenamento. undefined (type SistemaDeArquivoDeArmazenamentoDoJogador has no field or method )`

## Escreva código suficiente para fazer o teste rodar e veja o retorno do erro do teste

Precisamos adicionar o método para o novo tipo para fazer o teste compilar.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) (nome string) int {
    return 0
}
```

Agora compila e o teste falha

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/get_player_score
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador//get_player_score (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:43: recebido 0 esperado 33
```

## Escreva código sufience para fazer passar

Podemos iterar sobre a liga para encontrar o jogador e retornar a pontuação dele.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) (nome string) int {

    var vitorias int

    for _, jogador := range f.PegaLiga() {
        if jogador.Nome == nome {
            vitorias = jogador.Vitorias
            break
        }
    }

    return vitorias
}
```

## Refatore

Você terá visto vários refatoramentos de teste helper, então deixarei este para você fazer funcionar

```go
t.Run("/pega pontuacao do  jogador", func(t *testing.T) {
    bancoDeDados := strings.NewReader(`[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

    recebido := armazenamento.("Chris")
    esperado := 33
    definePontuacaoIgual(t, recebido, esperado)
})
```

Finalmente, precisamos começar a salvar pontuações com `SalvaVitoria`.

## Escreva o teste primeiro

Nossa abordagem é um pouco ruim para escritas. Não podemos (facilmente) apenas atualizar uma "linha" de JSON em um arquivo. Precisaremos armazenar a *inteira* nova representação de nosso banco de dados em cada escrita.

Como escrevemos? Normalmente usaríamos um `Writer`, mas já temos nosso `ReadSeeker`. Potencialmente podemos ter duas dependências, mas a biblioteca padrão já tem uma interface para nós: o `ReadWriteSeeker`, que permite fazermos tudo que precisamos com um arquivo.

Vamos atualizar nosso tipo:

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.ReadWriteSeeker
}
```

Veja se compila:

```go
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:15:34: cannot use bancoDeDados (type *strings.Reader) as type io.ReadWriteSeeker in field value:
    *strings.Reader does not implement io.ReadWriteSeeker (missing Write method)
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:36:34: cannot use bancoDeDados (type *strings.Reader) as type io.ReadWriteSeeker in field value:
    *strings.Reader does not implement io.ReadWriteSeeker (missing Write method)
```

Não é tão surpreendente que `strings.Reader` não implementa `ReadWriteSeeker`, então o que vamos fazer?

Temos duas opções:

* Criar um arquivo temporário para cada teste. `*os.File` implementa `ReadWriteSeeker`. O pró disso é que isso se torna mais um teste de integração, mas nós realmente estamos lendo e escrevendo de um sistema de arquivos então isso nos dará um alto nível de confiança. Os contras são que preferimos testes unitários porque são mais rápidos e normalmente mais simples. Também precisaremos trabalhar mais criando arquivos temporários e então ter certeza que serão removidos após o teste.
* Poderíamos usar uma biblioteca externa. [Mattetti](https://github.com/mattetti) escreveu uma biblioteca [filebuffer](https://github.com/mattetti/filebuffer) que implementa a interface que precisamos e assim não precisariamos modificar o sistema de arquivos.

Não acredito que exista uma resposta especialmente errada aqui, mas ao escolher usar uma biblioteca externa eu teria que explicar o gerenciamento de dependências! Então usaremos os arquivos.

Antes de adicionarmos nosso teste precisamos fazer nossos outros testes compilarem substituindo o `strings.Reader` com um `os.File`.

Vamos criar uma função auxiliar que irá criar um arquivo temporário com alguns dados dentro dele

```go
func criaArquivoTemporario(t *testing.T, dadoInicial string) (io.ReadWriteSeeker, func()) {
    t.Helper()

   arquivotmp, err := ioutil.TempFile("", "db")

    if err != nil {
        t.Fatalf("não foi possivel escrever o arquivo temporário %v", err)
    }

    arquivotmp.Write([]byte(dadoInicial))

    removeArquivo := func() {
        arquivotmp.Close()
        os.Remove(arquivotmp.Name())
    }

    return arquivotmp, removeArquivo
}
```

[TempFile](https://golang.org/pkg/io/ioutil/#TempDir) cria um arquivo temporário para usarmos. O valor `"db"` que passamos é um prefixo colocado em um arquivo de nome aleatório que vai criar. Isto é para garantir que não vai dar conflito acidental com outros arquivos.

Você irá notar que não estamos retornando apenas nosso `ReadWriteSeeker` (o arquivo) mas também uma função. Precisamos garantir que o arquivo é removido uma vez que o teste é finalizado. Não queremos que dados sejam vazados dos arquivos no teste como é possível acontecer e desinteressante para o leitor. Ao retornar uma função `removeArquivo` , cuidamos dos detalhes no nosso auxiliar e tudo que a chamada precisa fazer é executar `defer limpaBancoDeDados()`.

```go
func TestaArmazenamentoDeSistemaDeArquivo(t *testing.T) {

    t.Run("liga de um leitor", func(t *testing.T) {
        bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
            {"Nome": "Cleo", "Vitorias": 10},
            {"Nome": "Chris", "Vitorias": 33}]`)
        defer limpaBancoDeDados()

        armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

        recebido := armazenamento.PegaLiga()

        esperado := []Jogador{
            {"Cleo", 10},
            {"Chris", 33},
        }

        defineLiga(t, recebido, esperado)

        // ler novamente
        recebido = armazenamento.PegaLiga()
        defineLiga(t, recebido, esperado)
    })

    t.Run("retorna pontuação do jogador", func(t *testing.T) {
        bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
            {"Nome": "Cleo", "Vitorias": 10},
            {"Nome": "Chris", "Vitorias": 33}]`)
        defer limpaBancoDeDados()

        armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

        recebido := armazenamento.("Chris")
        esperado := 33
        definePontuacaoIgual(t, recebido, esperado)
    })
}
```

Rode os testes e eles devem estar passando! Teve uma quantidade razoável de mudanças mas agora parece que nossa definição de interface completa e deve ser muito fáci adicionar novos testes de agora em diante.

Vamos pegar a primeira iteração de gravar uma vitória de um jogador existente

```go
t.Run("armazena vitórias de um jogador existente", func(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)
    defer limpaBancoDeDados()

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

    armazenamento.SalvaVitoria("Chris")

    recebido := armazenamento.("Chris")
    esperado := 34
    definePontuacaoIgual(t, recebido, esperado)
})
```

## Tente rodar o teste

`./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:67:8: armazenamento.SalvaVitoria undefined (type SistemaDeArquivoDeArmazenamentoDoJogador has no field or method SalvaVitoria)`

## Escreva código suficiente para fazer o teste rodar e veja o retorno do erro do teste

Adicione um novo método

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {

}
```

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/store_wins_for_existing_players
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador/store_wins_for_existing_players (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:71: recebido 33 esperado 34
```

Nossa implementação está vazia então a pontuação anterior está sendo retornada.

## Escreva código sufience para fazer passar

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {
    liga := f.PegaLiga()

    for i, jogador := range liga {
        if jogador.Nome == nome {
            liga[i].Vitorias++
        }
    }

    f.bancoDeDados.Seek(0,0)
    json.NewEncoder(f.bancoDeDados).Encode(liga)
}
```

Você deve está se perguntando por que estou fazendo `liga[i].Vitorias++` invés de `jogador.Vitorias++`.

Quando você `percorre` sobre um pedaço é retornado o índice atual do laço (no nosso caso `i`) e uma *cópia* do elemento naquele índice. Mudando o valor `Vitorias` não irá afetar no pedaço `liga` que iteramos sobre. Por este motivo, precisamos pegar a referência do valor atual fazendo `liga[i]` e então mudando este valor.

Se rodar os testes, eles devem estar passando.

## Refatore

Em `PegaPontuacaoDoJogador` e `SalvaVitoria`, estamos iterando sobre `[]Jogador` para encontrar um jogador pelo nome.

Poderíamos refatorar esse código comum nos internos de `SistemaDeArquivoDeArmazenamentoDoJogador` mas para mim, parece que talvez seja um código util então poderíamos colocar em um novo tipo. Trabalhando com uma "Liga" até agora tem sido com `[]Jogador` mas podemos criar um novo tipo chamado `Liga`. Será mais fácil para outros desenvolvedores entenderem e assim podemos anexar métodos utéis dentro desse tipo para usarmos.

Dentro de `liga.go` adicionamos o seguinte

```go
type Liga []Jogador
func (l Liga) Find(nome string) *Jogador {
    for i, p := range l {
        if p.Nome==nome {
            return &l[i]
        }
    }
    return nil
}
```

Agora se qualquer um tiver uma `Liga` facilmente será encontrado um dado jogador.

Mude nossa interface `GuardaJogador` para retornar `Liga` invés de `[]Jogador`. Tente e rode novamente os teste, você terá um problema de compilação por termos modificado a interface mas é fácil de resolver; apenas modifique o tipo de retorno de `[]Jogador` to `Liga`.

Isso nos permite simplificar os métodos em `SistemaDeArquivoDeArmazenamentoDoJogador`.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) (nome string) int {

    jogador := f.PegaLiga().Find(nome)

    if  jogador != nil {
        return  jogador.Vitorias
    }

    return 0
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {
    liga := f.PegaLiga()
    jogador :=liga.Find(nome)

    if  jogador != nil {
        jogador.Vitorias++
    }

    f.bancoDeDados.Seek(0, 0)
    json.NewEncoder(f.bancoDeDados).Encode(liga)
}
```

Isto parece bem melhor and podemos ver como talvez possamos encontrar como outras funcionalidades úteis em torno de `Liga` podem ser refatoradas.

Agora precisamos tratar o cenário de salvar vitórias de novos jogadores.

## Escreva o teste primeiro

```go
t.Run("armazena vitorias de novos jogadores", func(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)
    defer limpaBancoDeDados()

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

    armazenamento.SalvaVitoria("Pepper")

    recebido := armazenamento.("Pepper")
    esperado := 1
    definePontuacaoIgual(t, recebido, esperado)
})
```

## Tente rodar o teste

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/store_wins_for_new_players#01
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador/store_wins_for_new_players#01 (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:86: recebido 0 esperado 1
```

## Escreva código suficiente para fazer passar

Apenas precisamos tratar o caso onde `Find` returna `nil` por não ter conseguido encontrar o jogador.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {
    liga := f.PegaLiga()
    jogador := liga.Find(nome)

    if jogador != nil {
        jogador.Wins++
    } else {
        liga = append(liga, Jogador{nome, 1})
    }

    f.bancoDeDados.Seek(0, 0)
    json.NewEncoder(f.bancoDeDados).Encode(liga)
}
```

O caminho feliz parece bom então agora vamos tentar usar nossa nova `armazenamento` no teste de integração. Isto nos dará mais confiança que o software funciona e então podemos deletar o redundante `NovoArmazenamentoDeJogadorNaMemoria`.

Em `TestRecordingWinsAndRetrievingThem` substitui a velha armazenamento.

```go
bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, "")
defer limpaBancoDeDados()
armazenamento := &SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}
```

Se você rodar o teste ele deve passar e agora podemos deletar `NovoArmazenamentoDeJogadorNaMemoria`. `main.go` terá problemas de compilação que nos motivará para agora usar nossa nova armazenamento no código "real".

```go
package main

import (
    "log"
    "net/http"
    "os"
)

const dbFileName = "game.db.json"

func main() {
    db, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        log.Fatalf("problema abrindo %s %v", dbFileName, err)
    }

    armazenamento := &SistemaDeArquivoDeArmazenamentoDoJogador{db}
    server := NovoServidorDoJogador(armazenamento)

    if err := http.ListenAndServe(":5000", server); err != nil {
        log.Fatalf("não foi possivel escutar na porta 5000 %v", err)
    }
}
```

* Nós criamos um arquivo para nosso banco de dados.
* O 2º argumento para `os.OpenFile` permite definir as permissões para abrir um arquivo, no nosso caso `O_RDWR` significa que queremos ler e escrever *e* `os.O_CREATE` significa criar um arquivo se ele não existe.
* O 3º argumento significa definir as permissões para o arquivo, no nosso caso, todos os usuários podem ler e escrever o arquivo. [(Veja superuser.com para uma explicação mais detalhada)](https://superuser.com/questions/295591/what-is-the-meaning-of-chmod-666).

Rodando o programa agora os dados permanecem em um arquivo entre reinicializações, uhu!

## Mais refatoramento e preocupações com performance

Toda vez que alguém chama `PegaLiga()` ou `()` estamos lendo o arquivo do ínicio, e transformando ele em JSON. Não deveríamos ter que fazer isso porque `SistemaDeArquivoDeArmazenamentoDoJogador` é inteiramente responsável pelo estado da liga; apenas queremos usar o arquivo para pegar o estado atual e atualiza-lo quando os dados mudarem.

Podemos criar um construtor que pode fazer parte dessa inicialização para nós e armazena a liga como um valor em nosso `SistemaDeArquivoDeArmazenamentoDoJogador` para ser usado nas leitura então.

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.ReadWriteSeeker
    liga Liga
}

func NovoSistemaDeArquivoDeArmazenamentoDoJogador(bancoDeDados io.ReadWriteSeeker) *SistemaDeArquivoDeArmazenamentoDoJogador {
    bancoDeDados.Seek(0, 0)
    liga, _ := NovaLiga(bancoDeDados)
    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados:bancoDeDados,
        liga:liga,
    }
}
```

Desta maneira precisamos ler do disco apenas uma vez . Podemos agora substituir todas as nossas chamadas anteriores para pegar a liga do disco e apenas usar `f.liga` no lugar.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() Liga {
    return f.liga
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) (nome string) int {

    jogador := f.liga.Find(nome)

    if jogador != nil {
        return jogador.Vitorias
    }

    return 0
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {
    jogador := f.liga.Find(nome)

    if jogador != nil {
        jogador.Vitorias++
    } else {
        f.liga = append(f.liga, Jogador{nome, 1})
    }

    f.bancoDeDados.Seek(0, 0)
    json.NewEncoder(f.bancoDeDados).Encode(f.liga)
}
```

Se você tentar e rodar os testes eles agora vão reclamar sobre inicializar `SistemaDeArquivoDeArmazenamentoDoJogador` então fixe-o chamando nosso construtor.

### Outro problema

Existe mais alguma ingenuidade na maneira como estamos lidando com arquivos que *poderiamos* criar um erro bem bobo futuramente.

Quando nós chamamos `SalvaVitoria` nós `procuramos` no ínicio do arquivo e então escrevemos o novo dado mas e se o novo dado for menor que o que estava lá antes?

Na nossa situação atual, isso é impossível. Nunca editamos ou apagamos pontuações, então os dados apenas podem aumentar, mas seria irresponsabilidade nossa deixar o código desse jeito, não é inimaginável que um cenário de apagamento poderia aparecer.

Como iremos testar isso então? O que precisamos fazer primeiro é refatorar nosso código, então separamos nossa preocupação do *tipo de dados que escrevemos, da escrita*. Podemos então testar isso separadamente para verificar se funciona como esperamos.

Agora iremos criar um novo tipo para encapsular nossa funcionalidade "quando escrevemos, vamos para o começo". Vou chama-la de `Fita`. Criamos um novo arquivo com o seguinte

```go
package main

import "io"

type fita struct {
    arquivo io.ReadWriteSeeker
}

func (t *fita) Write(p []byte) (n int, err error) {
    t.arquivo.Seek(0, 0)
    return t.arquivo.Write(p)
}
```

Note que apenas implementamos `Write` agora, já que encapsula a parte de `Procura` . Isso que dizer que `SistemaDeArquivoDeArmazenamentoDoJogador` pode ter uma referência a `Writer` invés disso.

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.Writer
    liga   Liga
}
```

Atualize o construtor para usar `fita`

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(bancoDeDados io.ReadWriteSeeker) *SistemaDeArquivoDeArmazenamentoDoJogador {
    bancoDeDados.Seek(0, 0)
    liga, _ := NovaLiga(bancoDeDados)

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: &fita{bancoDeDados},
        liga:   liga,
    }
}
```

Finalmente, podemos ter o incrível beneficio que queríamos removendo `Procura` de `SalvaVitoria`. Sim, não parece muito, mas pelo menos isso significa que, se fizermos qualquer outro tipo de escritas, podemos confiar no nosso `Write` para se comportar como precisamos. Além disso, agora podemos testar o potencial código problemático separadamente e corrigi-lo.

Agora vamos escrever o teste onde atualizamos todo o conteúdo de um arquivo com algo menor que o conteúdo original . Em `fita_test.go`:

## Escreva o teste primeiro

Vamos apenas criar um arquivo, tentar e escrever nele usando nossa fita, ler todo novamente e visualizar o que está no arquivo

```go
func TestaFita_Escrita(t *testing.T) {
    arquivo, limpa := criaArquivoTemporario(t, "12345")
    defer limpa()

    fita := &fita{arquivo}

    fita.Write([]byte("abc"))

    arquivo.Seek(0, 0)
    novoConteudoDoArquivo, _ := ioutil.ReadAll(arquivo)

    recebido := string(novoConteudoDoArquivo)
    esperado := "abc"

    if recebido != esperado {
        t.Errorf("recebido '%s' esperado '%s'", recebido, esperado)
    }
}
```

## Tente rodar o teste

```
=== RUN   TestaFita_Escrita
--- FAIL: TestaFita_Escrita (0.00s)
    fita_test.go:23: recebido 'abc45' esperado 'abc'
```

Como pensamos! Ele apenas escreve os dados que queremos, deixando todo o resto.

## Escreva código suficiente para fazer passar

`os.File` tem uma função truncada que vai permitir que o arquivo seja esvaziado eficientemente. Devemos ser capazes de apenas chama-la para conseguir o que queremos.

Mude `fita` para o seguinte

```go
type fita struct {
    file *os.File
}

func (t *fita) Write(p []byte) (n int, err error) {
    t.file.Truncate(0)
    t.file.Seek(0, 0)
    return t.file.Write(p)
}
```

O compilador irá falhar em alguns lugares quando esperamos um `io.ReadWriteSeeker` mas estamos mandando um `*os.File`. Você deve ser capaz de corrigir esses problemas por conta própria, mas se ficar preso basta checar o código fonte.

Uma vez que você tenha refatorado nosso teste `TestaFita_Escrita` deve estar passando!

### Uma outra pequena refatoração

Em `SalvaVitoria` temos uma linha`json.NewEncoder(f.bancoDeDados).Encode(f.league)`.

Não precisamos criar um novo codificador toda vez que escrevemos, podemos inicializar um em nosso construtor e usa-lo.

Armazena uma referência para um `Encoder` para nosso tipo.

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados *json.Encoder
    liga   Liga
}
```

Inicialize no construtor

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(arquivo *os.File) *SistemaDeArquivoDeArmazenamentoDoJogador {
    arquivo.Seek(0, 0)
    liga, _ := NovaLiga(arquivo)

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: json.NewEncoder(&fita{arquivo}),
        liga:   liga,
    }
}
```

Use em `SalvaVitoria`.

## Não quebramos algumas regras ali? Testando coisas privadas? Sem interfaces?

### Testando tipos privados

É verdade que *no geral* deve ser favorecido não testar coisas privadas, uma vez que isso, as vezes, leva a testar coisas bastante acopladas para a implementação; que pode impedir refatoramento no futuro.

Entretanto,não devemos esquecer que testes nos dá *confiança*.

Não estamos confiantes que nossa implementação funcionaria se tivéssemos adicionado algum tipo de funcionalidade para editar ou deletar. Não queremos deixar o código assim, especialmente se isso foi trabalhado por mais de uma pessoa que talvez não estivesse ciente dos defeitos da nossa abordagem.

Finalmente, é apenas um teste! Se decidirmos mudar a maneira como funciona não será um desastre deletar o teste, mas teremos que ter pego o requisito para futuro mantenedores.

### Interfaces

Começamos o código usando `io.Reader` como o caminho mais fácil para testar de forma unitária nosso novo `GuardaJogador`. A medida que desenvolvemos nosso código, movemos para `io.ReadWriter` e então para `io.ReadWriteSeeker`. Descobrimos então que não tinha nada na biblioteca padrão que implementasse isso além de `*os.File`. Poderiamos ter decidido escrever o nosso ou usar um de código aberto, mas isso pareceu pragmático apenas para fazer arquivos temporários para os testes.

Finalmente, precisamos de `Truncate` que também está no `*os.File`. Isso seria uma opção para criar nossa própria interface pegando esses requisitos.

```go
type ReadWriteSeekTruncate interface {
    io.ReadWriteSeeker
    Truncate(size int64) error
}
```

Mas o que isso está realmente nos dando? Lembre-se que *não estamos mockando* e isso é irrealista para um armazenamento de **sistema de arquivos** receber outro tipo além que um `*os.File` então não precisamos do polimorfismo que interface nos dá.

Não tenha medo de cortar e mudar tipos e experimentar como temos aqui. O bom de usar uma linguagem tipada estaticamente é o compilador que ajudará você com toda mudança.

## Tratamento de erros

Antes de começarmos no ordenamento, devemos ter certeza que estamos contentes com nosso código atual e remover qualquer débito técnico que ainda resta. É um principio importante para trabalhar com software o mais rápido possível (mantenha-se fora do estado vermelho) mas isso não quer dizer que devemos ignorar os casos de erro!

Se voltarmos para `SistemaDeArquivoDeArmazenamentoDoJogador.go` temos `liga, _ := NovaLiga(f.bancoDeDados)` no nosso construtor.

`NovaLiga`pode retornar um erro se é instável passar a liga do `io.Reader` que fornecemos.

Era pragmático ignorar isso naquela hora como já tinhamos testes falhando. Se tivemos tentado lidar com isso ao mesmo tempo estamos lidando com duas coisas de uma vez.

Vamos fazer com que nosso construtor seja capaz de retornar um erro.

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(arquivo *os.File) (*SistemaDeArquivoDeArmazenamentoDoJogador, error) {
    arquivo.Seek(0, 0)
    liga, err := NovaLiga(arquivo)

    if err != nil {
        return nil, fmt.Errorf("problema carregando o armazenamento do jogador  de arquivo %s, %v", arquivo.Nome(), err)
    }

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: json.NewEncoder(&fita{arquivo}),
        liga:   liga,
    }, nil
}
```

Lembre-se que é importante retornar mensagens de erro úteis (assim como nossos testes). As pessoas na internet dizem que a maioria dos códigos em Go é

```go
if err != nil {
    return err
}
```

**Isso é 100% não idiomático.** Adicionando informação contextual (i.e o que você estava fazendo que causou o erro\\) para suas mensagens de erro facilita manipular o software.

Se você tentar e compilar, vai ver alguns erros.

```
./main.go:18:35: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:35:36: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:57:36: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:70:36: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:85:36: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./server_integration_test.go:12:35: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
```

Em main vamos querer sair do programa, imprimindo o erro.

```go
armazenamento, err := NovoSistemaDeArquivoDeArmazenamentoDoJogador(db)

if err != nil {
    log.Fatalf("problema criando o sistema de arquivo do armazenamento do jogador, %v ", err)
}
```

Nos nossos testes podemos garantir que não exista erro . Podemos fazer uma função auxiliar para ajudar com isto.

```go
func defineSemErro(t *testing.T, err error) {
    t.Helper()
    if err != nil {
        t.Fatalf("não esperava um erro mas obteve um, %v", err)
    }
}
```

Trabalhe nos outros problemas de compilação usando essa auxiliar. Finalmente, você deve ter um teste falhando

```
=== RUN   TestRecordingWinsAndRetrievingThem
--- FAIL: TestRecordingWinsAndRetrievingThem (0.00s)
    server_integration_test.go:14: não esperava um erro mas obteve um, problema carregando o armazenamento do jogador  de arquivo /var/folders/nj/r_ccbj5d7flds0sf63yy4vb80000gn/T/db841037437, problem parsing league, EOF
```

Não podemos analisar a liga porque o arquivo está vazio.Não estávamos obtendo erros antes porque sempre os ignoramos.

Vamos corrigir nosso grande teste de integração colocando algum JSON válido nele e então podemos escrever um teste específico para este cenário.

```go
func TestRecordingWinsAndRetrievingThem(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[]`)
    //etc...
```

Agora todos os testes estão passando, precisamos então lidar com o cenário onde o arquivo está vazio.

## Escreva o teste primeiro

```go
t.Run("funciona com um arquivo vazio", func(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, "")
    defer limpaBancoDeDados()

    _, err := NovoSistemaDeArquivoDeArmazenamentoDoJogador(bancoDeDados)

    defineSemErro(t, err)
})
```

## Tente rodar o teste

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/works_with_an_empty_file
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador/works_with_an_empty_file (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:108: não esperava um erro mas obteve um, problema carregando o armazenamento do jogador  de arquivo /var/folders/nj/r_ccbj5d7flds0sf63yy4vb80000gn/T/db019548018, problem parsing league, EOF
```

## Escreva código sufience para fazer passar

Mude nosso construtor para o seguinte

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(arquivo *os.File) (*SistemaDeArquivoDeArmazenamentoDoJogador, error) {

    arquivo.Seek(0, 0)

    info, err := arquivo.Stat()

    if err != nil {
        return nil, fmt.Errorf("problema ao usar o arquivo  %s, %v", arquivo.Nome(), err)
    }

    if info.Size() == 0 {
        file.Write([]byte("[]"))
        file.Seek(0, 0)
    }

    liga, err := NovaLiga(file)

    if err != nil {
        return nil, fmt.Errorf("problema carregando armazenamento de jogador do aquivo %s, %v", arquivo.Nome(), err)
    }

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: json.NewEncoder(&fita{file}),
        liga:   liga,
    }, nil
}
```

`Arquivo.Stat` retorna estatísticas do nosso arquivo. Isto nos permite checar o tamanho do arquivo, se está vazio podemos `Escrever` um array JSON vazio e `Busca` de volta para o ínicio, pronto para o resto do arquivo.

## Refatore

Nosso construtor está um pouco bagunçado, podemos extrair o código de inicialização em uma função

```go
func iniciaArquivoBDDeJogador(arquivo *os.File) error {
    arquivo.Seek(0, 0)

    info, err := arquivo.Stat()

    if err != nil {
        return fmt.Errorf("problema ao usar arquivo %s, %v", file.Name(), err)
    }

    if info.Size()==0 {
        arquivo.Write([]byte("[]"))
        arquivo.Seek(0, 0)
    }

    return nil
}
```

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(arquivo *os.File) (*SistemaDeArquivoDeArmazenamentoDoJogador, error) {

    err := iniciaArquivoBDDeJogador(file)

    if err != nil {
        return nil, fmt.Errorf("problema inicializando arquivo do jogador, %v", err)
    }

    liga, err := Nova(liga)

    if err != nil {
        return nil, fmt.Errorf("problema carregando armazenamento de jogador do arquivo %s, %v", arquivo.Nome(), err)
    }

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: json.NewEncoder(&fita{file}),
        liga:   liga,
    }, nil
}
```

## Ordenação

Nossa dona do produto quer que `/liga` retorne os jogadores ordenados pela pontuação.

A principal decisão a ser feita é onde isso deve acontecer no software. Se estamos usando um "verdadeiro" banco de dados usariamos coisas como `ORDER BY` , então o ordenamento é super rápido por esse motivo parece que a implementção de `GuardaJogador` deve ser responsável.

## Escreva o teste primeiro

Podemos atualizar a inserção no nosso primeiro teste em `TestaArmazenamentoDeSistemaDeArquivo`

```go
t.Run("liga ordernada", func(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)
    defer limpaBancoDeDados()

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

   recebido := armazenamento.PegaLiga()

   esperado:= []Jogador{
        {"Chris", 33},
        {"Cleo", 10},
    }

    defineLiga(t, recebido, esperado)

    // read again
    recebido = armazenamento.PegaLiga()
    defineLiga(t, recebido, esperado)
})
```

A ordem que está sendo recebida do JSON está errada e nosso `esperado` vai checar que é retornado para o chamador na ordem correta.

## Tente rodar o teste

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/league_from_a_reader,_sorted
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador/league_from_a_reader,_sorted (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:46: recebido [{Cleo 10} {Chris 33}] esperado [{Chris 33} {Cleo 10}]
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:51: recebido [{Cleo 10} {Chris 33}] esperado [{Chris 33} {Cleo 10}]
```

## Escreva código sufience para fazer passar

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() League {
    sort.Slice(f.liga, func(i, j int) bool {
        return f.liga[i].Vitorias > f.liga[j].Vitorias
    })
    return f.liga
}
```

[`sort.Slice`](https://golang.org/pkg/sort/#Slice)

> Slice ordena a parte fornecida dada a menor função fornecida

Moleza!

## Finalizando

### O que cobrimos

* A interface `Seeker` e sua relação com `Reader` e `Writer`.
* Trabalhando com arquivos.
* Criando uma auxiliar fácil de usar para testes com arquivos que escondem todas as bagunças.
* `sort.Slice` para ordenar partes.
* Usando o compilador para nos ajudar a fazer mudanças estruturais de forma segura na aplicação.

### Quebrando regras

* Maior partes das regras em engenharia de software não são realmente regras, apenas boas práticas que funcionam 80% do tempo.
* Descobrimos um cenário onde nos "regras" anteriores de não testar funções internas não foi útil, então quebramos essa regra.
* É importante entender o que estamos perdendo e ganhado ao quebrar as regras . No nosso caso, não tinha problema porque era apenas um teste e seria muito difícil exercitar o cenário contrário.
* Para poder quebrar as regras, **você deve entende-las**. Uma analogia é com aprender a tocar violão. Não importa quão criativo você seja, você deve entender e praticar os fundamentos.

### Onde nosso software está

* Temos uma API HTTP onde é possível criar jogadores e aumentar a pontuação deles..
* Podemos retornar uma liga das pontuações de todos como JSON.
* O dado é mantindo com um arquivo JSON.


# Linha de comando e estrutura de pacotes

[**Você pode encontrar os exemplos deste capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/criando-uma-aplicacao/linha-de-comando)

Nosso gerente de produto quer [*pivotar*](https://pt.wikipedia.org/wiki/Startup#Dicion%C3%A1rio_com_os_termos_mais_usados_pelas_startups) e introduzir uma segunda aplicação - uma aplicação de linha de comando.

Inicialmente, ela vai apenas ser capaz de gravar o que um jogador vence quando o usuário digita `Ruth venceu`. A intenção é eventualmente criar uma ferramenta para ajudar usuários a jogar pôquer.

O gerente de produto quer que o banco de dados seja compartilhado entre as duas aplicações para que a `liga` atualize de acordo com as vitórias gravadas nessa nova aplicação.

## Lembrando do código

Nós temos uma aplicação com um arquivo `main.go` que inicia um servidor HTTP. O servidor HTTP não é nosso interesse neste exercício mas a abstração usada é. Ele depende de `ArmazenamentoJogador`.

```go
type ArmazenamentoJogador interface {
    ObterPontuacaoDeJogador(nome string) int
    GravarVitoria(nome string)
    ObterLiga() Liga
}
```

No capítulo anterior, criamos um `SistemaDeArquivoArmazenamentoJogador` que implementa essa mesma interface. Temos que poder reutilizar parte dela para a nossa nova aplicação.

## Primeiro vamos [refatorar](https://pt.wikipedia.org/wiki/Refatora%C3%A7%C3%A3o) um pouco

Nosso projeto precisa criar dois executáveis, nosso existente servidor web e o app de linha de comando.

Antes de nos entretermos no nosso novo código, precisamos estruturar nosso projeto melhor para suportar isso.

Até agora todos os códigos foram colocador em uma única pasta, em uma estrutura parecida com essa

`$GOPATH/src/github.com/seu-nome/meu-app`

Para fazer qualquer aplicação em Go, é necessário uma função `main` dentro de um `package main`. Até agora todo nosso código viveu dentro de `package main` e a função `func main` pode referenciar tudo.

Isso foi legal e é uma boa prática não sair gerando estrutura com pacotes logo de início. Se você olhar dentro da biblioteca padrão você vai ver bem pouco a utilização de pastas e estruturas.

Felizmente é bem fácil adicionar uma estrutura *quando precisar dela*.

Dentro do projeto existente crie uma pasta `cmd` com uma chamada `webserver` dentro dela (ex: `mkdir -p cmd/webserver`).

Mova o arquivo `main.go` para dentro dessa pasta.

Se você tiver o comando `tree` instalado você pode executar sua estrutura de pastas tem que parecer

```
.
├── ArmazenamentoSistemaArquivo.go
├── ArmazenamentoSistemaArquivo_test.go
├── cmd
│   └── webserver
│       └── main.go
├── liga.go
├── servidor.go
├── servidor_integration_test.go
├── servidor_test.go
├── tape.go
└── tape_test.go
```

Agora temos uma separação efetiva entre nossa aplicação e o código da biblioteca mas agora temos que mudar alguns nomes de pacotes(package). Lembre-se que ao construir uma aplicação Go seu nome *deve* ser `main`.

Mude todos os outros códigos para ter um pacote chamado `poquer`.

Finalmente, temos que importar esse pacote no `main.go` para utilizá-lo na criação de nosso servidor web. Então podemos usar nossa biblioteca chamando `poquer.NomeDaFunção`.

Os caminhos de diretórios vão ser diferentes no seu computador, mas deveria parecer com isso:

```go
package main

import (
    "log"
    "net/http"
    "os"
    "github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v1"
)

const nomeArquivoBD = "jogo.db.json"

func main() {
    db, err := os.OpenFile(nomeArquivoBD, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        log.Fatalf("falha ao abrir %s %v", nomeArquivoBD, err)
    }

    armazenamento, err := poquer.NovoArmazenamentoSistemaDeArquivodeJogador(db)

    if err != nil {
        log.Fatalf("falha ao criar sistema de arquivos para armazenar jogadores, %v ", err)
    }

    servidor := poquer.NovoServidorJogador(armazenamento)

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("nao foi possivel escutar na porta 5000 %v", err)
    }
}
```

O caminho da pasta pode parecer chocante, mas essa é a forma para importar *qualquer* biblioteca pública no seu código.

Separando nosso código em um pacote isolado e enviando para um repositório público como o GitHub qualquer desenvolvedor Go pode escrever código que importe esse pacote com as funcionalidades que disponibilizarmos. A primeira vez que você tentar e executar ele vai reclamar que o pacote não existe mas tudo que precisa ser feito é executar `go get`.

[Além disso, usuários podem ver a documentação em godoc.org](https://godoc.org/github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v1).

### Verificações finais

* Dentro do diretório raiz rode `go test` e valide que ainda está passando
* Vá dentro de `cmd/webserver` e rode `go run main.go`
  * Abra `http://localhost:5000/liga` e veja que ainda está funcionando

### Estrutura inicial

Antes de escrever os testes, vamos adicionar uma nova aplicação que nosso projeto vai construir. Crie outro diretório dentro de `cmd` chamado `cli` (command line interface) e adicione um arquivo `main.go` com

```go
package main

import "fmt"

func main() {
    fmt.Println("Vamos jogar poquer")
}
```

O primeiro requisito que vamos discutir is como gravar uma vitória quando o usuário digitar `{NomeDoJogador} venceu`.

## Escreva o teste antes

Sabemos que temos que escrever algo chamado `CLI` que vai nos permitir `Jogar` poquer. Isso vai precisar ler o que o usuário digita e então gravar a vitória no armazenamento `ArmazenamentoJogador`.

Antes de irmos muito longe, vamos apenas escrever um teste para verificar a integração com a `ArmazenamentoJogador` funciona como gostaríamos.

Dentro de `CLI_test.go` (no diretório raiz do projeto, não dentro de `cmd`)

```go
func TestCLI(t *testing.T) {
    armazenamentoJogador := &EsbocoArmazenamentoJogador{}
    cli := &CLI{armazenamentoJogador}
    cli.JogarPoquer()

    if len(armazenamentoJogador.ChamadasDeVitoria) !=1 {
        t.Fatal("esperando uma chamada de vitoria mas nao recebi nenhuma")
    }
}
```

* Podemos usar nossa `EsbocoArmazenamentoJogador` de outros testes
* Passamos nossa dependência dentro do nosso ainda não existente tipo `CLI`
* Iniciamos o jogo chamando um método que chamaremos de `JogarPoquer`
* Validamos se a vitória foi registrada

## Tente rodar o teste

```
# github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v2
./cli_test.go:25:10: undefined: CLI
```

## Escreva o mínimo código para o teste rodar e verificarmos o próximo error

Neste ponto, você deveria estar confortável para criar nossa nova `CLI` struct (estrutura de dados) com os respectivos campos necessários para nossa dependência e adicionar um método.

Você deveria acabar com um código como esse

```go
type CLI struct {
    armazenamentoJogador ArmazenamentoJogador
}

func (cli *CLI) JogarPoquer() {}
```

Lembre-se que estamos apenas tentando fazer o teste rodar para validarmos que ele falha como esperamos

```
--- FAIL: TestCLI (0.00s)
    cli_test.go:30: esperando uma chamada de vitoria mas nao recebi nenhuma
FAIL
```

## Escreva código suficiente para fazer ele passar

```go
func (cli *CLI) JogarPoquer() {
    cli.armazenamentoJogador.GravarVitoria("Cleo")
}
```

Isso deve fazer ele passar.

Agora, precisamos simular lendo isso from `Stdin` (o que o usuário digita) para que fique registrado vitórias para jogadores específicos.

Vamos incrementar nosso teste para exercitar essa condição.

## Escreva o teste antes

```go
func TestCLI(t *testing.T) {
    in := strings.NewReader("Chris venceu\n")
    armazenamentoJogador := &EsbocoArmazenamentoJogador{}

    cli := &CLI{armazenamentoJogador, in}
    cli.JogarPoquer()

    if len(armazenamentoJogador.ChamadasDeVitoria) < 1 {
        t.Fatal("esperando uma chamada de vitoria mas nao recebi nenhuma")
    }

    obtido := armazenamentoJogador.ChamadasDeVitoria[0]
    esperado := "Chris"

    if obtido != esperado {
        t.Errorf("nao armazenou o vencedor correto, recebi '%s', esperava '%s'", obtido, esperado)
    }
}
```

`os.Stdin` é o que vamos usar no `main` para capturar o que for digitado pelo usuário. Ele é um `*File` por trás dos panos o que siginifica que implementa `io.Reader` o qual sabemos ser um jeito útil de capturar texto.

Nós criamos um `io.Reader` no nosso teste usando `strings.NewReader`, preenchendo ele com o que esperamos que o usuário digite.

## Tente rodar o teste

`./CLI_test.go:12:32: too many values in struct initializer`

Muitos valores no inicializador da estrutura.

## Escreva o mínimo código para o teste rodar e verificarmos o próximo error

Precisamos adicionar nossa nova dependência dentro de `CLI`.

```go
type CLI struct {
    armazenamentoJogador ArmazenamentoJogador
    in io.Reader
}
```

## Escreva código suficiente para fazer ele passar

```
--- FAIL: TestCLI (0.00s)
    CLI_test.go:23: nao armazenou o vencedor correto, recebi 'Cleo', esperava 'Chris'
FAIL
```

Lembre-se de primeiro fazer o que for mais fácil

```go
func (cli *CLI) JogarPoquer() {
    cli.armazenamentoJogador.GravarVitoria("Chris")
}
```

O teste vai passar. Depois nós vamos adicionar outro teste que vai nos forçar a escrever mais código, mas antes, vamos [refatorar](https://pt.wikipedia.org/wiki/Refatora%C3%A7%C3%A3o).

## [Refatoração](https://pt.wikipedia.org/wiki/Refatora%C3%A7%C3%A3o)

No `server_test` anteriormente fizemos validações para saber se uma vitória é armazenada assim como temos aqui. Vamos mover essa validação para dentro de um helper e manter o código [DRY](https://pt.wikipedia.org/wiki/Don%27t_repeat_yourself).

```go
func verificaVitoriaJogador(t *testing.T, armazenamento *EsbocoArmazenamentoJogador, vencedor string) {
    t.Helper()

    if len(armazenamento.ChamadasDeVitoria) != 1 {
        t.Fatalf("recebi %d chamadas de GravarVitoria esperava %d", len(armazenamento.ChamadasDeVitoria), 1)
    }

    if armazenamento.ChamadasDeVitoria[0] != vencedor {
        t.Errorf("nao armazenou o vencedor correto, recebi '%s' esperava '%s'", armazenamento.ChamadasDeVitoria[0], vencedor)
    }
}
```

Agora troque a validação em ambos os arquivos `server_test.go` e `CLI_test.go`.

O teste deve agora parecer com

```go
func TestCLI(t *testing.T) {
    in := strings.NewReader("Chris venceu\n")
    armazenamentoJogador := &EsbocoArmazenamentoJogador{}

    cli := &CLI{armazenamentoJogador, in}
    cli.JogarPoquer()

    verificaVitoriaJogador(t, armazenamentoJogador, "Chris")
}
```

Agora vamos escrever *outro* teste com uma variação do que o usuário digitou nos forçando a ler de verdade.

## Escreva o teste antes

```go
func TestCLI(t *testing.T) {

    t.Run("recorda vencedor chris digitado pelo usuario", func(t *testing.T) {
        in := strings.NewReader("Chris venceu\n")
        armazenamentoJogador := &EsbocoArmazenamentoJogador{}

        cli := &CLI{armazenamentoJogador, in}
        cli.JogarPoquer()

        verificaVitoriaJogador(t, armazenamentoJogador, "Chris")
    })

    t.Run("recorda vencedor cleo digitado pelo usuario", func(t *testing.T) {
        in := strings.NewReader("Cleo venceu\n")
        armazenamentoJogador := &EsbocoArmazenamentoJogador{}

        cli := &CLI{armazenamentoJogador, in}
        cli.JogarPoquer()

        verificaVitoriaJogador(t, armazenamentoJogador, "Cleo")
    })

}
```

## Tente rodar o teste

```
=== RUN   TestCLI
--- FAIL: TestCLI (0.00s)
=== RUN   TestCLI/recorda_vencedor_chris_digitado_pelo_usuario
    --- PASS: TestCLI/recorda_vencedor_chris_digitado_pelo_usuario (0.00s)
=== RUN   TestCLI/recorda_vencedor_cleo_digitado_pelo_usuario
    --- FAIL: TestCLI/recorda_vencedor_cleo_digitado_pelo_usuario (0.00s)
        CLI_test.go:27: nao armazenou o vencedor correto, recebi 'Chris' esperava 'Cleo'
FAIL
```

## Escreva código suficiente para fazer ele passar

Vamos usar o [`bufio.Scanner`](https://golang.org/pkg/bufio/) para ler o que foi digitado no `io.Reader`.

> O pacote bufio implementa [buffered](https://pt.wikipedia.org/wiki/Buffer_\(ci%C3%AAncia_da_computa%C3%A7%C3%A3o\)) [I/O](https://pt.wikipedia.org/wiki/Entrada/sa%C3%ADda). Ele encapsula um objeto io.Reader ou io.Writer, criando um outro objeto (Reader ou Writer) que também implementa a interface mas prover buffering e ajuda com entradas/saídas de textos.

Atualize o código para

```go
type CLI struct {
    armazenamentoJogador ArmazenamentoJogador
    in          io.Reader
}

func (cli *CLI) JogarPoquer() {
    reader := bufio.NewScanner(cli.in)
    reader.Scan()
    cli.armazenamentoJogador.GravarVitoria(extrairVencedor(reader.Text()))
}

func extrairVencedor(userInput string) string {
    return strings.Replace(userInput, " venceu", "", 1)
}
```

O teste agora vai passar.

* `Scanner.Scan()` vai ler até o carácter de nova linha.
* Só então usamos `Scanner.Text()` para returnar a `string` lida pelo scanner.

Agora que temos alguns testes passando, devemos amarrar isso ao nosso `main`. Lembre-se que devemos sempre almejar ter o código funcionando totalmente integrado o mais rápido que pudermos.

No `main.go` adicione o seguinte e execute. (você pode ter que ajustar o caminho da segunda dependência para refletir o que tem no seu computador)

```go
package main

import (
    "fmt"
    "github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v3"
    "log"
    "os"
)

const nomeArquivoBD = "jogo.db.json"

func main() {
    fmt.Println("Vamos jogar poquer")
    fmt.Println("Digite {Nome} venceu para registrar uma vitoria")

    db, err := os.OpenFile(nomeArquivoBD, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        log.Fatalf("falha ao abrir %s %v", nomeArquivoBD, err)
    }

    armazenamento, err := poquer.NovoArmazenamentoSistemaDeArquivodeJogador(db)

    if err != nil {
        log.Fatalf("falha ao criar sistema de arquivos para armazenar jogadores, %v ", err)
    }

    jogo := poquer.CLI{armazenamento, os.Stdin}
    jogo.JogarPoquer()
}
```

Você deve receber um erro:

```
linha-de-comando/v3/cmd/cli/main.go:32:25: implicit assignment of unexported field 'armazenamentoJogador' in poquer.CLI literal
linha-de-comando/v3/cmd/cli/main.go:32:34: implicit assignment of unexported field 'in' in poquer.CLI literal
```

O que está acontecendo é que por causa da tentativa de associar os campos `armazenamentoJogador` e `in` na `CLI`. Eles são campos não exportados(privados). Nós *podemos* fazer isso nos nossos testes porque o teste está no mesmo pacote da `CLI` (`poquer`). Mas nosso `main` é um pacote `main` portanto não tem acesso.

Isso enfatiza a importância de *integrar seu código*. Nós definimos corretamente as dependências da `CLI` como privada (porque não queremos expô-las para os usuários da `CLI`) mas não criamos uma forma para os usuário construí-las.

Existe alguma forma de identificarmos esse problema antes?

### `package mypackage_test`

Nos exemplos usados até agora, quando nós fazemos um arquivo para testes nós declaramos ele como pertencendo ao mesmo pacote que estamos testando.

Tudo bem e fazer isso significa no pior dos casos que queremos testar algo que é pertecente somente aquele pacote conseguimos acesso aos tipos não exportados.

Mas considerando que, *em geral*, advogamos para *não* se fazer testes de coisas internas, como Go pode garantir isso? E se pudéssemos testar nosso código aonde somente temos acesso aos tipos exportados (como em nossp `main`)?

Quando você escreve um project com múltiplos pacotes eu recomendo fortmente que o nome to seu pacote tenha o sufixo `_test`. Fazendo isso você somente ter acesso aos tipos públicos no seu pacote. Isso ajuda nesse caso especificamente mas também ajuda a disciplinar o teste somente de APIs públicas. Se ainda assim você precisar testar coisa interna você pode criar um teste separado com o nome de pacote igual ao do que você quer testar.

A máxima do TDD é que se você não pode testar o seu código então provávelmente vai ser difícil para os usuários do seu código de integrar com ele. Fazendo uso de `package foo_test` vai forçar você à testar seu código como se você estivesse importando ele como vão fazer aqueles que importarem o seu pacote.

Antes de consertar o `main` vamos mudar o nome de pacote do nosso teste dentro de `CLI_test.go` para `poquer_test`.

Se sua IDE estiver bem configurada você vai de repente ver um monte de vermelho! Se você rodar o compilador vocês vai ver os seguintes errors:

```
./CLI_test.go:12:19: undefined: EsbocoArmazenamentoJogador
./CLI_test.go:17:3: undefined: verificaVitoriaJogador
./CLI_test.go:22:19: undefined: EsbocoArmazenamentoJogador
./CLI_test.go:27:3: undefined: verificaVitoriaJogador
```

Nós agora tropeçamenos nos problemas de desenho do pacote. Para testar nosso código nós criamos algumas funções auxíliares e tipos emulados sem exportá-los e portanto não estão mais disponíveis para uso no nosso `CLI_test` porque eles foram definidos somente nos arquivos com `_test.go` no pacote `poquer`.

#### Queremos ter as funções auxíliares e tipos emulados disponível publicamente?

Está é uma discussão subjetiva. One argumento é que não queremos poluir a API do nosso pacote só para ter código que facilitam os tests.

Na apresentação ["Testes avançados em Go"](https://speakerdeck.com/mitchellh/advanced-testing-with-go?slide=53) do Mitchell Hashimoto, é descrito como eles advogam na HashiCorp isso para que usuários do pacote possam escrever testes sem ter que reinventar a roda escrevendo tipos emulados. No nosso caso, isso significa que qualquer um usando nosso pacote `poquer` não tem que criar seus próprios `ArmazenamentoJogador` emulados se eles quiserem usar nosso código.

Informalmente eu tenho usado esta técnica em outros pacotes compartilhados e tem se provado extremamente útil em termos de economizar tempo dos usuários quando eles integram com nossos pacotes.

Então vamos criar um arquivo chamado `testing.go` e adicionar nossos cógidos auxiliares nele.

```go
package poquer

import "testing"

type EsbocoArmazenamentoJogador struct {
    pontuacoes   map[string]int
    chamadasDeVitoria []string
    liga   []Jogador
}

func (s *EsbocoArmazenamentoJogador) ObterPontuacaoDeJogador(nome string) int {
    pontuacao := s.pontuacoes[nome]
    return pontuacao
}

func (s *EsbocoArmazenamentoJogador) GravarVitoria(nome string) {
    s.chamadasDeVitoria = append(s.chamadasDeVitoria, nome)
}

func (s *EsbocoArmazenamentoJogador) ObterLiga() Liga {
    return s.liga
}

func VerificaVitoriaJogador(t *testing.T, armazenamento *EsbocoArmazenamentoJogador, vencedor string) {
    t.Helper()

    if len(armazenamento.ChamadasDeVitoria) != 1 {
        t.Fatalf("recebi %d chamadas de GravarVitoria esperava %d", len(armazenamento.ChamadasDeVitoria), 1)
    }

    if armazenamento.ChamadasDeVitoria[0] != vencedor {
        t.Errorf("nao armazenou o vencedor correto, recebi '%s' esperava '%s'", armazenamento.ChamadasDeVitoria[0], vencedor)
    }
}

// tarefa para você - adicionar os códigos restantes
```

Você precisar tornar essas funções públicas (lembre-se que exportar em Go é feito apenas colocando a primeira letra em maíusculo) se você quiser que elas sejam expostas para quem importar esse pacote.

No nosso teste `CLI` você precisa chamar o código como se fosse usando de um pacote diferente.

```go
func TestCLI(t *testing.T) {

    t.Run("recorda vencedor chris digitado pelo usuario", func(t *testing.T) {
        in := strings.NewReader("Chris venceu\n")
        armazenamentoJogador := &poquer.EsbocoArmazenamentoJogador{}

        cli := &poquer.CLI{armazenamentoJogador, in}
        cli.JogarPoquer()

        poquer.VerificaVitoriaJogador(t, armazenamentoJogador, "Chris")
    })

    t.Run("recorda vencedor cleo digitado pelo usuario", func(t *testing.T) {
        in := strings.NewReader("Cleo venceu\n")
        armazenamentoJogador := &poquer.EsbocoArmazenamentoJogador{}

        cli := &poquer.CLI{armazenamentoJogador, in}
        cli.JogarPoquer()

        poquer.VerificaVitoriaJogador(t, armazenamentoJogador, "Cleo")
    })

}
```

Você vai ver que agora temos o mesmo problema que tivemos na `main`

```
./CLI_test.go:15:26: implicit assignment of unexported field 'armazenamentoJogador' in poquer.CLI literal
./CLI_test.go:15:39: implicit assignment of unexported field 'in' in poquer.CLI literal
./CLI_test.go:25:26: implicit assignment of unexported field 'armazenamentoJogador' in poquer.CLI literal
./CLI_test.go:25:39: implicit assignment of unexported field 'in' in poquer.CLI literal
```

O jeito mais fácil de resolver isso é fazer um construtor como temos para outros tipos. Nós também vamos mudar o `CLI` para que ele armazene a `bufio.Scanner` ao invés do leitor pois ele vai ser automaticamente encapsulado no momento da construção.

```go
type CLI struct {
    armazenamentoJogador ArmazenamentoJogador
    in          *bufio.Scanner
}

func NovoCLI(armazenamento ArmazenamentoJogador, in io.Reader) *CLI {
    return &CLI{
        armazenamentoJogador: armazenamento,
        in:          bufio.NewScanner(in),
    }
}
```

Fazendo isso, podemos simplificar e refatorar no código do leitor

```go
func (cli *CLI) JogarPoquer() {
    userInput := cli.readLine()
    cli.armazenamentoJogador.GravarVitoria(extrairVencedor(userInput))
}

func extrairVencedor(userInput string) string {
    return strings.Replace(userInput, " venceu", "", 1)
}

func (cli *CLI) readLine() string {
    cli.in.Scan()
    return cli.in.Text()
}
```

Mude o teste para usar o esse construtor e valtamos a ter nossos testes passando.

Por último, podemos voltar para o nosso `main.go` e usar o construtor que acabamos de criar

```go
jogo := poquer.NovoCLI(armazenamento, os.Stdin)
```

Tente executar ele, digite "Bob venceu".

### Refatoração

Nós temos alguma repetição nas nossas respectivas aplicações aonde estamos abrindo um arquivo e criando um `ArmazenamentoSistemaArquivo` a partir do seu conteúdo. Isso parece uma pequena fraqueza no desenho do nosso pacote então deveríamos fazer uma função nele para encapsular a abertura de arquivos dado um caminho e retornar a `ArmazenamentoJogador`.

```go
func ArmazenamentoSistemaDeArquivoJogadorAPartirDeArquivo(path string) (*SistemaDeArquivoArmazenamentoJogador, func(), error) {
    db, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        return nil, nil, fmt.Errorf("falha ao abrir %s %v", path, err)
    }

    closeFunc := func() {
        db.Close()
    }

    armazenamento, err := NovoArmazenamentoSistemaDeArquivodeJogador(db)

    if err != nil {
        return nil, nil, fmt.Errorf("falha ao criar sistema de arquivos para armazenar jogadores, %v ", err)
    }

    return armazenamento, closeFunc, nil
}
```

Agora refatorando ambas aplicações para usar a função de criar o armazenamento.

#### Código da aplicação CLI

```go
package main

import (
    "github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v3"
    "log"
    "os"
    "fmt"
)

const nomeArquivoBD = "jogo.db.json"

func main() {
    armazenamento, close, err := poquer.ArmazenamentoSistemaDeArquivoJogadorAPartirDeArquivo(nomeArquivoBD)

    if err != nil {
        log.Fatal(err)
    }
    defer close()

    fmt.Println("Vamos jogar poquer")
    fmt.Println("Digite {Nome} venceu para registrar uma vitoria")
    poquer.NovoCLI(armazenamento, os.Stdin).JogarPoquer()
}
```

#### Código da aplicação do servidor Web

```go
package main

import (
    "github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v3"
    "log"
    "net/http"
)

const nomeArquivoBD = "jogo.db.json"

func main() {
    armazenamento, close, err := poquer.ArmazenamentoSistemaDeArquivoJogadorAPartirDeArquivo(nomeArquivoBD)

    if err != nil {
        log.Fatal(err)
    }
    defer close()

    servidor := poquer.NovoServidorJogador(armazenamento)

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("nao foi possivel escutar na porta 5000 %v", err)
    }
}
```

Note a simetria: mesmo sendo diferente interfaces de usuário o setup é quase idêntico. Isso dá impressão de uma boa validação do nosso desenho. E note também que `ArmazenamentoSistemaDeArquivoJogadorAPartirDeArquivo` retorna uma função `close` (fechar), que podemos encerrar o arquivo fundamental assim que terminarmos de usar o armazenamento.

## Resumindo

### Estrutura do pacote

Esse capítulo pretendia criar duas aplicações, reusar o código de domínio que escrevemos até agora. Para fazer isso, nós precisamos atualizar a estrutura do nosso pacote para que ela tivesse pastas separadas para nossos respectivos `main`s.

Fazendo isso nós enfrentamos problemas de integração devido a valores não exportados então demostrando o valor de trabalhar em pequenas "etapas" e integrar com frequência.

Aprendemos como `mypackage_test` ajudou a criar um ambiente de testes que prover a mesma experiência de outros pacotes integrando com nosso código, assim ajudando você a pegar problemas de integração e ver o quão fácil (ou não) é de usar seu código.

### Lendo a entrada do usuário

Vimos como lendo do `os.Stdin` é muito fácil de usar pois ele implementa o `io.Reader`. Nós usamos `bufio.Scanner` para facilitar a leitura linha à linha do que o usuário digita.

### Abstração simples leva à simples reutilização de código

Quase não nos esforçamos para integrar a `ArmazenamentoJogador` na nossa aplicação (assim que fizemos alguns ajustes no pacode) e subsequente testar foi muito fácil tambem porque nós decidimos também expor a versão emulada.


# Tempo

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

The product owner wants us to expand the functionality of our command line application by helping a group of people play Texas-Holdem Poker.

## Just enough information on poker

You wont need to know much about poker, only that at certain time intervals all the players need to be informed of a steadily increasing "blind" value.

Our application will help keep track of when the blind should go up, and how much it should be.

* When it starts it asks how many players are playing. This determines the amount of time there is before the "blind" bet goes up.
  * There is a base amount of time of 5 minutes.
  * For every player, 1 minute is added.
  * e.g 6 players equals 11 minutes for the blind.
* After the blind time expires the game should alert the players the new amount the blind bet is.
* The blind starts at 100 chips, then 200, 400, 600, 1000, 2000 and continue to double until the game ends (our previous functionality of "Ruth wins" should still finish the game)

## Reminder of the code

In the previous chapter we made our start to the command line application which already accepts a command of `{name} wins`. Here is what the current `CLI` code looks like, but be sure to familiarise yourself with the other code too before starting.

```go
type CLI struct {
    playerStore PlayerStore
    in          *bufio.Scanner
}

func NewCLI(store PlayerStore, in io.Reader) *CLI {
    return &CLI{
        playerStore: store,
        in:          bufio.NewScanner(in),
    }
}

func (cli *CLI) PlayPoker() {
    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}

func extractWinner(userInput string) string {
    return strings.Replace(userInput, " wins", "", 1)
}

func (cli *CLI) readLine() string {
    cli.in.Scan()
    return cli.in.Text()
}
```

### `time.AfterFunc`

We want to be able to schedule our program to print the blind bet values at certain durations dependant on the number of players.

To limit the scope of what we need to do, we'll forget about the number of players part for now and just assume there are 5 players so we'll test that *every 10 minutes the new value of the blind bet is printed*.

As usual the standard library has us covered with [`func AfterFunc(d Duration, f func()) *Timer`](https://golang.org/pkg/time/#AfterFunc)

> `AfterFunc` waits for the duration to elapse and then calls f in its own goroutine. It returns a `Timer` that can be used to cancel the call using its Stop method.

### [`time.Duration`](https://golang.org/pkg/time/#Duration)

> A Duration represents the elapsed time between two instants as an int64 nanosecond count.

The time library has a number of constants to let you multiply those nanoseconds so they're a bit more readable for the kind of scenarios we'll be doing

```go
5 * time.Second
```

When we call `PlayPoker` we'll schedule all of our blind alerts.

Testing this may be a little tricky though. We'll want to verify that each time period is scheduled with the correct blind amount but if you look at the signature of `time.AfterFunc` its second argument is the function it will run. You cannot compare functions in Go so we'd be unable to test what function has been sent in. So we'll need to write some kind of wrapper around `time.AfterFunc` which will take the time to run and the amount to print so we can spy on that.

## Write the test first

Add a new test to our suite

```go
t.Run("it schedules printing of blind values", func(t *testing.T) {
    in := strings.NewReader("Chris wins\n")
    playerStore := &poker.StubPlayerStore{}
    blindAlerter := &SpyBlindAlerter{}

    cli := poker.NewCLI(playerStore, in, blindAlerter)
    cli.PlayPoker()

    if len(blindAlerter.alerts) != 1 {
        t.Fatal("expected a blind alert to be scheduled")
    }
})
```

You'll notice we've made a `SpyBlindAlerter` which we are trying to inject into our `CLI` and then checking that after we call `PlayerPoker` that an alert is scheduled.

(Remember we are just going for the simplest scenario first and then we'll iterate.)

Here's the definition of `SpyBlindAlerter`

```go
type SpyBlindAlerter struct {
    alerts []struct{
        scheduledAt time.Duration
        amount int
    }
}

func (s *SpyBlindAlerter) ScheduleAlertAt(duration time.Duration, amount int) {
    s.alerts = append(s.alerts, struct {
        scheduledAt time.Duration
        amount int
    }{duration,  amount})
}
```

## Try to run the test

```
./CLI_test.go:32:27: too many arguments in call to poker.NewCLI
    have (*poker.StubPlayerStore, *strings.Reader, *SpyBlindAlerter)
    want (poker.PlayerStore, io.Reader)
```

## Write the minimal amount of code for the test to run and check the failing test output

We have added a new argument and the compiler is complaining. *Strictly speaking* the minimal amount of code is to make `NewCLI` accept a `*SpyBlindAlerter` but let's cheat a little and just define the dependency as an interface.

```go
type BlindAlerter interface {
    ScheduleAlertAt(duration time.Duration, amount int)
}
```

And then add it to the constructor

```go
func NewCLI(store PlayerStore, in io.Reader, alerter BlindAlerter) *CLI
```

Your other tests will now fail as they dont have a `BlindAlerter` passed in to `NewCLI`.

Spying on BlindAlerter is not relevant for the other tests so in the test file add

```go
var dummySpyAlerter = &SpyBlindAlerter{}
```

Then use that in the other tests to fix the compilation problems. By labelling it as a "dummy" it is clear to the reader of the test that it is not important.

[> Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.](https://martinfowler.com/articles/mocksArentStubs.html)

The tests should now compile and our new test fails.

```
=== RUN   TestCLI
=== RUN   TestCLI/it_schedules_printing_of_blind_values
--- FAIL: TestCLI (0.00s)
    --- FAIL: TestCLI/it_schedules_printing_of_blind_values (0.00s)
        CLI_test.go:38: expected a blind alert to be scheduled
```

## Write enough code to make it pass

We'll need to add the `BlindAlerter` as a field on our `CLI` so we can reference it in our `PlayPoker` method.

```go
type CLI struct {
    playerStore PlayerStore
    in          *bufio.Reader
    alerter     BlindAlerter
}

func NewCLI(store PlayerStore, in io.Reader, alerter BlindAlerter) *CLI {
    return &CLI{
        playerStore: store,
        in:          bufio.NewReader(in),
        alerter:     alerter,
    }
}
```

To make the test pass, we can call our `BlindAlerter` with anything we like

```go
func (cli *CLI) PlayPoker() {
    cli.alerter.ScheduleAlertAt(5 * time.Second, 100)
    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}
```

Next we'll want to check it schedules all the alerts we'd hope for, for 5 players

## Write the test first

```go
    t.Run("it schedules printing of blind values", func(t *testing.T) {
        in := strings.NewReader("Chris wins\n")
        playerStore := &poker.StubPlayerStore{}
        blindAlerter := &SpyBlindAlerter{}

        cli := poker.NewCLI(playerStore, in, blindAlerter)
        cli.PlayPoker()

        cases := []struct{
            expectedScheduleTime time.Duration
            expectedAmount       int
        } {
            {0 * time.Second, 100},
            {10 * time.Minute, 200},
            {20 * time.Minute, 300},
            {30 * time.Minute, 400},
            {40 * time.Minute, 500},
            {50 * time.Minute, 600},
            {60 * time.Minute, 800},
            {70 * time.Minute, 1000},
            {80 * time.Minute, 2000},
            {90 * time.Minute, 4000},
            {100 * time.Minute, 8000},
        }

        for i, c := range cases {
            t.Run(fmt.Sprintf("%d scheduled for %v", c.expectedAmount, c.expectedScheduleTime), func(t *testing.T) {

                if len(blindAlerter.alerts) <= i {
                    t.Fatalf("alert %d was not scheduled %v", i, blindAlerter.alerts)
                }

                alert := blindAlerter.alerts[i]

                amountGot := alert.amount
                if amountGot != c.expectedAmount {
                    t.Errorf("got amount %d, want %d", amountGot, c.expectedAmount)
                }

                gotScheduledTime := alert.scheduledAt
                if gotScheduledTime != c.expectedScheduleTime {
                    t.Errorf("got scheduled time of %v, want %v", gotScheduledTime, c.expectedScheduleTime)
                }
            })
        }
    })
```

Table-based test works nicely here and clearly illustrate what our requirements are. We run through the table and check the `SpyBlindAlerter` to see if the alert has been scheduled with the correct values.

## Try to run the test

You should have a lot of failures looking like this

```go
=== RUN   TestCLI
--- FAIL: TestCLI (0.00s)
=== RUN   TestCLI/it_schedules_printing_of_blind_values
    --- FAIL: TestCLI/it_schedules_printing_of_blind_values (0.00s)
=== RUN   TestCLI/it_schedules_printing_of_blind_values/100_scheduled_for_0s
        --- FAIL: TestCLI/it_schedules_printing_of_blind_values/100_scheduled_for_0s (0.00s)
            CLI_test.go:71: got scheduled time of 5s, want 0s
=== RUN   TestCLI/it_schedules_printing_of_blind_values/200_scheduled_for_10m0s
        --- FAIL: TestCLI/it_schedules_printing_of_blind_values/200_scheduled_for_10m0s (0.00s)
            CLI_test.go:59: alert 1 was not scheduled [{5000000000 100}]
```

## Write enough code to make it pass

```go
func (cli *CLI) PlayPoker() {

    blinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}
    blindTime := 0 * time.Second
    for _, blind := range blinds {
        cli.alerter.ScheduleAlertAt(blindTime, blind)
        blindTime = blindTime + 10 * time.Minute
    }

    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}
```

It's not a lot more complicated than what we already had. We're just now iterating over an array of `blinds` and calling the scheduler on an increasing `blindTime`

## Refactor

We can encapsulate our scheduled alerts into a method just to make `PlayPoker` read a little clearer.

```go
func (cli *CLI) PlayPoker() {
    cli.scheduleBlindAlerts()
    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}

func (cli *CLI) scheduleBlindAlerts() {
    blinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}
    blindTime := 0 * time.Second
    for _, blind := range blinds {
        cli.alerter.ScheduleAlertAt(blindTime, blind)
        blindTime = blindTime + 10*time.Minute
    }
}
```

Finally our tests are looking a little clunky. We have two anonymous structs representing the same thing, a `ScheduledAlert`. Let's refactor that into a new type and then make some helpers to compare them.

```go
type scheduledAlert struct {
    at time.Duration
    amount int
}

func (s scheduledAlert) String() string {
    return fmt.Sprintf("%d chips at %v", s.amount, s.at)
}

type SpyBlindAlerter struct {
    alerts []scheduledAlert
}

func (s *SpyBlindAlerter) ScheduleAlertAt(at time.Duration, amount int) {
    s.alerts = append(s.alerts, scheduledAlert{at, amount})
}
```

We've added a `String()` method to our type so it prints nicely if the test fails

Update our test to use our new type

```go
t.Run("it schedules printing of blind values", func(t *testing.T) {
    in := strings.NewReader("Chris wins\n")
    playerStore := &poker.StubPlayerStore{}
    blindAlerter := &SpyBlindAlerter{}

    cli := poker.NewCLI(playerStore, in, blindAlerter)
    cli.PlayPoker()

    cases := []scheduledAlert {
        {0 * time.Second, 100},
        {10 * time.Minute, 200},
        {20 * time.Minute, 300},
        {30 * time.Minute, 400},
        {40 * time.Minute, 500},
        {50 * time.Minute, 600},
        {60 * time.Minute, 800},
        {70 * time.Minute, 1000},
        {80 * time.Minute, 2000},
        {90 * time.Minute, 4000},
        {100 * time.Minute, 8000},
    }

    for i, want := range cases {
        t.Run(fmt.Sprint(want), func(t *testing.T) {

            if len(blindAlerter.alerts) <= i {
                t.Fatalf("alert %d was not scheduled %v", i, blindAlerter.alerts)
            }

            got := blindAlerter.alerts[i]
            assertScheduledAlert(t, got, want)
        })
    }
})
```

Implement `assertScheduledAlert` yourself.

We've spent a fair amount of time here writing tests and have been somewhat naughty not integrating with our application. Let's address that before we pile on any more requirements.

Try running the app and it wont compile, complaining about not enough args to `NewCLI`.

Let's create an implementation of `BlindAlerter` that we can use in our application.

Create `BlindAlerter.go` and move our `BlindAlerter` interface and add the new things below

```go
package poker

import (
    "time"
    "fmt"
    "os"
)

type BlindAlerter interface {
    ScheduleAlertAt(duration time.Duration, amount int)
}

type BlindAlerterFunc func(duration time.Duration, amount int)

func (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {
    a(duration, amount)
}

func StdOutAlerter(duration time.Duration, amount int) {
    time.AfterFunc(duration, func() {
        fmt.Fprintf(os.Stdout, "Blind is now %d\n", amount)
    })
}
```

Remember that any *type* can implement an interface, not just `structs`. If you are making a library that exposes an interface with one function defined it is a common idiom to also expose a `MyInterfaceFunc` type.

This type will be a `func` which will also implement your interface. That way users of your interface have the option to implement your interface with just a function; rather than having to create an empty `struct` type.

We then create the function `StdOutAlerter` which has the same signature as the function and just use `time.AfterFunc` to schedule it to print to `os.Stdout`.

Update `main` where we create `NewCLI` to see this in action

```go
poker.NewCLI(store, os.Stdin, poker.BlindAlerterFunc(poker.StdOutAlerter)).PlayPoker()
```

Before running you might want to change the `blindTime` increment in `CLI` to be 10 seconds rather than 10 minutes just so you can see it in action.

You should see it print the blind values as we'd expect every 10 seconds. Notice how you can still type `Shaun wins` into the CLI and it will stop the program how we'd expect.

The game wont always be played with 5 people so we need to prompt the user to enter a number of players before the game starts.

## Write the test first

To check we are prompting for the number of players we'll want to record what is written to StdOut. We've done this a few times now, we know that `os.Stdout` is an `io.Writer` so we can check what is written if we use dependency injection to pass in a `bytes.Buffer` in our test and see what our code will write.

We don't care about our other collaborators in this test just yet so we've made some dummies in our test file.

We should be a little wary that we now have 4 dependencies for `CLI`, that feels like maybe it is starting to have too many responsibilities. Let's live with it for now and see if a refactoring emerges as we add this new functionality.

```go
var dummyBlindAlerter = &SpyBlindAlerter{}
var dummyPlayerStore = &poker.StubPlayerStore{}
var dummyStdIn = &bytes.Buffer{}
var dummyStdOut = &bytes.Buffer{}
```

Here is our new test

```go
t.Run("it prompts the user to enter the number of players", func(t *testing.T) {
    stdout := &bytes.Buffer{}
    cli := poker.NewCLI(dummyPlayerStore, dummyStdIn, stdout, dummyBlindAlerter)
    cli.PlayPoker()

    got := stdout.String()
    want := "Please enter the number of players: "

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }
})
```

We pass in what will be `os.Stdout` in `main` and see what is written.

## Try to run the test

```
./CLI_test.go:38:27: too many arguments in call to poker.NewCLI
    have (*poker.StubPlayerStore, *bytes.Buffer, *bytes.Buffer, *SpyBlindAlerter)
    want (poker.PlayerStore, io.Reader, poker.BlindAlerter)
```

## Write the minimal amount of code for the test to run and check the failing test output

We have a new dependency so we'll have to update `NewCLI`

```go
func NewCLI(store PlayerStore, in io.Reader, out io.Writer, alerter BlindAlerter) *CLI
```

Now the *other* tests will fail to compile because they dont have an `io.Writer` being passed into `NewCLI`.

Add `dummyStdout` for the other tests.

The new test should fail like so

```
=== RUN   TestCLI
--- FAIL: TestCLI (0.00s)
=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players
    --- FAIL: TestCLI/it_prompts_the_user_to_enter_the_number_of_players (0.00s)
        CLI_test.go:46: got '', want 'Please enter the number of players: '
FAIL
```

## Write enough code to make it pass

We need to add our new dependency to our `CLI` so we can reference it in `PlayPoker`

```go
type CLI struct {
    playerStore PlayerStore
    in          *bufio.Reader
    out         io.Writer
    alerter     BlindAlerter
}

func NewCLI(store PlayerStore, in io.Reader, out io.Writer, alerter BlindAlerter) *CLI {
    return &CLI{
        playerStore: store,
        in:          bufio.NewReader(in),
        out:         out,
        alerter:     alerter,
    }
}
```

Then finally we can write our prompt at the start of the game

```go
func (cli *CLI) PlayPoker() {
    fmt.Fprint(cli.out, "Please enter the number of players: ")
    cli.scheduleBlindAlerts()
    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}
```

## Refactor

We have a duplicate string for the prompt which we should extract into a constant

```go
const PlayerPrompt = "Please enter the number of players: "
```

Use this in both the test code and `CLI`.

Now we need to send in a number and extract it out. The only way we'll know if it has had the desired effect is by seeing what blind alerts were scheduled.

## Write the test first

```go
t.Run("it prompts the user to enter the number of players", func(t *testing.T) {
    stdout := &bytes.Buffer{}
    in := strings.NewReader("7\n")
    blindAlerter := &SpyBlindAlerter{}

    cli := poker.NewCLI(dummyPlayerStore, in, stdout, blindAlerter)
    cli.PlayPoker()

    got :=stdout.String()
    want := poker.PlayerPrompt

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }

    cases := []scheduledAlert{
        {0 * time.Second, 100},
        {12 * time.Minute, 200},
        {24 * time.Minute, 300},
        {36 * time.Minute, 400},
    }

    for i, want := range cases {
        t.Run(fmt.Sprint(want), func(t *testing.T) {

            if len(blindAlerter.alerts) <= i {
                t.Fatalf("alert %d was not scheduled %v", i, blindAlerter.alerts)
            }

            got := blindAlerter.alerts[i]
            assertScheduledAlert(t, got, want)
        })
    }
})
```

Ouch! A lot of changes.

* We remove our dummy for StdIn and instead send in a mocked version representing our user entering 7
* We also remove our dummy on the blind alerter so we can see that the number of players has had an effect on the scheduling
* We test what alerts are scheduled

## Try to run the test

The test should still compile and fail reporting that the scheduled times are wrong because we've hard-coded for the game to be based on having 5 players

```
=== RUN   TestCLI
--- FAIL: TestCLI (0.00s)
=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players
    --- FAIL: TestCLI/it_prompts_the_user_to_enter_the_number_of_players (0.00s)
=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players/100_chips_at_0s
        --- PASS: TestCLI/it_prompts_the_user_to_enter_the_number_of_players/100_chips_at_0s (0.00s)
=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players/200_chips_at_12m0s
```

## Write enough code to make it pass

Remember, we are free to commit whatever sins we need to make this work. Once we have working software we can then work on refactoring the mess we're about to make!

```go
func (cli *CLI) PlayPoker() {
    fmt.Fprint(cli.out, PlayerPrompt)

    numberOfPlayers, _ := strconv.Atoi(cli.readLine())

    cli.scheduleBlindAlerts(numberOfPlayers)

    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}

func (cli *CLI) scheduleBlindAlerts(numberOfPlayers int) {
    blindIncrement := time.Duration(5 + numberOfPlayers) * time.Minute

    blinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}
    blindTime := 0 * time.Second
    for _, blind := range blinds {
        cli.alerter.ScheduleAlertAt(blindTime, blind)
        blindTime = blindTime + blindIncrement
    }
}
```

* We read in the `numberOfPlayersInput` into a string
* We use `cli.readLine()` to get the input from the user and then call `Atoi` to convert it into an integer - ignoring any error scenarios. We'll need to write a test for that scenario later.
* From here we change `scheduleBlindAlerts` to accept a number of players. We then calculate a `blindIncrement` time to use to add to `blindTime` as we iterate over the blind amounts

While our new test has been fixed, a lot of others have failed because now our system only works if the game starts with a user entering a number. You'll need to fix the tests by changing the user inputs so that a number followed by a newline is added (this is highlighting yet more flaws in our approach right now).

## Refactor

This all feels a bit horrible right? Let's **listen to our tests**.

* In order to test that we are scheduling some alerts we set up 4 different dependencies. Whenever you have a lot of dependencies for a *thing* in your system, it implies it's doing too much. Visually we can see it in how cluttered our test is.
* To me it feels like **we need to make a cleaner abstraction between reading user input and the business logic we want to do**
* A better test would be *given this user input, do we call a new type `Game` with the correct number of players*.
* We would then extract the testing of the scheduling into the tests for our new `Game`.

We can refactor toward our `Game` first and our test should continue to pass. Once we've made the structural changes we want we can think about how we can refactor the tests to reflect our new separation of concerns

Remember when making changes in refactoring try to keep them as small as possible and keep re-running the tests.

Try it yourself first. Think about the boundaries of what a `Game` would offer and what our `CLI` should be doing.

For now **don't** change the external interface of `NewCLI` as we don't want to change the test code and the client code at the same time as that is too much to juggle and we could end up breaking things.

This is what I came up with:

```go
// game.go
type Game struct {
    alerter BlindAlerter
    store   PlayerStore
}

func (p *Game) Start(numberOfPlayers int) {
    blindIncrement := time.Duration(5+numberOfPlayers) * time.Minute

    blinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}
    blindTime := 0 * time.Second
    for _, blind := range blinds {
        p.alerter.ScheduleAlertAt(blindTime, blind)
        blindTime = blindTime + blindIncrement
    }
}

func (p *Game) Finish(winner string) {
    p.store.RecordWin(winner)
}

// cli.go
type CLI struct {
    playerStore PlayerStore
    in          *bufio.Reader
    out         io.Writer
    game        *Game
}

func NewCLI(store PlayerStore, in io.Reader, out io.Writer, alerter BlindAlerter) *CLI {
    return &CLI{
        in:  bufio.NewReader(in),
        out: out,
        game: &Game{
            alerter: alerter,
            store:   store,
        },
    }
}

const PlayerPrompt = "Please enter the number of players: "

func (cli *CLI) PlayPoker() {
    fmt.Fprint(cli.out, PlayerPrompt)

    numberOfPlayersInput := cli.readLine()
    numberOfPlayers, _ := strconv.Atoi(strings.Trim(numberOfPlayersInput, "\n"))

    cli.game.Start(numberOfPlayers)

    winnerInput := cli.readLine()
    winner := extractWinner(winnerInput)

    cli.game.Finish(winner)
}

func extractWinner(userInput string) string {
    return strings.Replace(userInput, " wins\n", "", 1)
}

func (cli *CLI) readLine() string {
    cli.in.Scan()
    return cli.in.Text()
}
```

From a "domain" perspective:

* We want to `Start` a `Game`, indicating how many people are playing
* We want to `Finish` a `Game`, declaring the winner

The new `Game` type encapsulates this for us.

With this change we've passed `BlindAlerter` and `PlayerStore` to `Game` as it is now responsible for alerting and storing results.

Our `CLI` is now just concerned with:

* Constructing `Game` with its existing dependencies (which we'll refactor next)
* Interpreting user input as method invocations for `Game`

We want to try to avoid doing "big" refactors which leave us in a state of failing tests for extended periods as that increases the chances of mistakes. (If you are working in a large/distributed team this is extra important)

The first thing we'll do is refactor `Game` so that we inject it into `CLI`. We'll do the smallest changes in our tests to facilitate that and then we'll see how we can break up the tests into the themes of parsing user input and game management.

All we need to do right now is change `NewCLI`

```go
func NewCLI(in io.Reader, out io.Writer, game *Game) *CLI {
    return &CLI{
        in:  bufio.NewReader(in),
        out: out,
        game: game,
    }
}
```

This feels like an improvement already. We have less dependencies and *our dependency list is reflecting our overall design goal* of CLI being concerned with input/output and delegating game specific actions to a `Game`.

If you try and compile there are problems. You should be able to fix these problems yourself. Don't worry about making any mocks for `Game` right now, just initialise *real* `Game`s just to get everything compiling and tests green.

To do this you'll need to make a constructor

```go
func NewGame(alerter BlindAlerter, store PlayerStore) *Game {
    return &Game{
        alerter:alerter,
        store:store,
    }
}
```

Here's an example of one of the setups for the tests being fixed

```go
stdout := &bytes.Buffer{}
in := strings.NewReader("7\n")
blindAlerter := &SpyBlindAlerter{}
game := poker.NewGame(blindAlerter, dummyPlayerStore)

cli := poker.NewCLI(in, stdout, game)
cli.PlayPoker()
```

It shouldn't take much effort to fix the tests and be back to green again (that's the point!) but make sure you fix `main.go` too before the next stage.

```go
// main.go
game := poker.NewGame(poker.BlindAlerterFunc(poker.StdOutAlerter), store)
cli := poker.NewCLI(os.Stdin, os.Stdout, game)
cli.PlayPoker()
```

Now that we have extracted out `Game` we should move our game specific assertions into tests separate from CLI.

This is just an exercise in copying our `CLI` tests but with less dependencies

```go
func TestGame_Start(t *testing.T) {
    t.Run("schedules alerts on game start for 5 players", func(t *testing.T) {
        blindAlerter := &poker.SpyBlindAlerter{}
        game := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)

        game.Start(5)

        cases := []poker.ScheduledAlert{
            {At: 0 * time.Second, Amount: 100},
            {At: 10 * time.Minute, Amount: 200},
            {At: 20 * time.Minute, Amount: 300},
            {At: 30 * time.Minute, Amount: 400},
            {At: 40 * time.Minute, Amount: 500},
            {At: 50 * time.Minute, Amount: 600},
            {At: 60 * time.Minute, Amount: 800},
            {At: 70 * time.Minute, Amount: 1000},
            {At: 80 * time.Minute, Amount: 2000},
            {At: 90 * time.Minute, Amount: 4000},
            {At: 100 * time.Minute, Amount: 8000},
        }

        checkSchedulingCases(cases, t, blindAlerter)
    })

    t.Run("schedules alerts on game start for 7 players", func(t *testing.T) {
        blindAlerter := &poker.SpyBlindAlerter{}
        game := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)

        game.Start(7)

        cases := []poker.ScheduledAlert{
            {At: 0 * time.Second, Amount: 100},
            {At: 12 * time.Minute, Amount: 200},
            {At: 24 * time.Minute, Amount: 300},
            {At: 36 * time.Minute, Amount: 400},
        }

        checkSchedulingCases(cases, t, blindAlerter)
    })

}

func TestGame_Finish(t *testing.T) {
    store := &poker.StubPlayerStore{}
    game := poker.NewTexasHoldem(dummyBlindAlerter, store)
    winner := "Ruth"

    game.Finish(winner)
    poker.AssertPlayerWin(t, store, winner)
}
```

The intent behind what happens when a game of poker starts is now much clearer.

Make sure to also move over the test for when the game ends.

Once we are happy we have moved the tests over for game logic we can simplify our CLI tests so they reflect our intended responsibilities clearer

* Process user input and call `Game`'s methods when appropriate
* Send output
* Crucially it doesn't know about the actual workings of how games work

To do this we'll have to make it so `CLI` no longer relies on a concrete `Game` type but instead accepts an interface with `Start(numberOfPlayers)` and `Finish(winner)`. We can then create a spy of that type and verify the correct calls are made.

It's here we realise that naming is awkward sometimes. Rename `Game` to `TexasHoldem` (as that's the *kind* of game we're playing) and the new interface will be called `Game`. This keeps faithful to the notion that our CLI is oblivious to the actual game we're playing and what happens when you `Start` and `Finish`.

```go
type Game interface {
    Start(numberOfPlayers int)
    Finish(winner string)
}
```

Replace all references to `*Game` inside `CLI` and replace them with `Game` (our new interface). As always keep re-running tests to check everything is green while we are refactoring.

Now that we have decoupled `CLI` from `TexasHoldem` we can use spies to check that `Start` and `Finish` are called when we expect them to, with the correct arguments.

Create a spy that implements `Game`

```go
type GameSpy struct {
    StartedWith  int
    FinishedWith string
}

func (g *GameSpy) Start(numberOfPlayers int) {
    g.StartedWith = numberOfPlayers
}

func (g *GameSpy) Finish(winner string) {
    g.FinishedWith = winner
}
```

Replace any `CLI` test which is testing any game specific logic with checks on how our `GameSpy` is called. This will then reflect the responsibilities of CLI in our tests clearly.

Here is an example of one of the tests being fixed; try and do the rest yourself and check the source code if you get stuck.

```go
    t.Run("it prompts the user to enter the number of players and starts the game", func(t *testing.T) {
        stdout := &bytes.Buffer{}
        in := strings.NewReader("7\n")
        game := &GameSpy{}

        cli := poker.NewCLI(in, stdout, game)
        cli.PlayPoker()

        gotPrompt := stdout.String()
        wantPrompt := poker.PlayerPrompt

        if gotPrompt != wantPrompt {
            t.Errorf("got '%s', want '%s'", gotPrompt, wantPrompt)
        }

        if game.StartCalledWith != 7 {
            t.Errorf("wanted Start called with 7 but got %d", game.StartCalledWith)
        }
    })
```

Now that we have a clean separation of concerns, checking edge cases around IO in our `CLI` should be easier.

We need to address the scenario where a user puts a non numeric value when prompted for the number of players:

Our code should not start the game and it should print a handy error to the user and then exit.

## Write the test first

We'll start by making sure the game doesn't start

```go
t.Run("it prints an error when a non numeric value is entered and does not start the game", func(t *testing.T) {
        stdout := &bytes.Buffer{}
        in := strings.NewReader("Pies\n")
        game := &GameSpy{}

        cli := poker.NewCLI(in, stdout, game)
        cli.PlayPoker()

        if game.StartCalled {
            t.Errorf("game should not have started")
        }
    })
```

You'll need to add to our `GameSpy` a field `StartCalled` which only gets set if `Start` is called

## Try to run the test

```
=== RUN   TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game
    --- FAIL: TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game (0.00s)
        CLI_test.go:62: game should not have started
```

## Write enough code to make it pass

Around where we call `Atoi` we just need to check for the error

```go
numberOfPlayers, err := strconv.Atoi(cli.readLine())

if err != nil {
    return
}
```

Next we need to inform the user of what they did wrong so we'll assert on what is printed to `stdout`.

## Write the test first

We've asserted on what was printed to `stdout` before so we can copy that code for now

```go
gotPrompt := stdout.String()

wantPrompt := poker.PlayerPrompt + "you're so silly"

if gotPrompt != wantPrompt {
    t.Errorf("got '%s', want '%s'", gotPrompt, wantPrompt)
}
```

We are storing *everything* that gets written to stdout so we still expect the `poker.PlayerPrompt`. We then just check an additional thing gets printed. We're not too bothered about the exact wording for now, we'll address it when we refactor.

## Try to run the test

```
=== RUN   TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game
    --- FAIL: TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game (0.00s)
        CLI_test.go:70: got 'Please enter the number of players: ', want 'Please enter the number of players: you're so silly'
```

## Write enough code to make it pass

Change the error handling code

```go
if err != nil {
    fmt.Fprint(cli.out, "you're so silly")
    return
}
```

## Refactor

Now refactor the message into a constant like `PlayerPrompt`

```go
wantPrompt := poker.PlayerPrompt + poker.BadPlayerInputErrMsg
```

and put in a more appropriate message

```go
const BadPlayerInputErrMsg = "Bad value received for number of players, please try again with a number"
```

Finally our testing around what has been sent to `stdout` is quite verbose, let's write an assert function to clean it up.

```go
func assertMessagesSentToUser(t *testing.T, stdout *bytes.Buffer, messages ...string) {
    t.Helper()
    want := strings.Join(messages, "")
    got := stdout.String()
    if got != want {
        t.Errorf("got '%s' sent to stdout but expected %+v", got, messages)
    }
}
```

Using the vararg syntax (`...string`) is handy here because we need to assert on varying amounts of messages.

Use this helper in both of the tests where we assert on messages sent to the user.

There are a number of tests that could be helped with some `assertX` functions so practice your refactoring by cleaning up our tests so they read nicely.

Take some time and think about the value of some of the tests we've driven out. Remember we don't want more tests than necessary, can you refactor/remove some of them *and still be confident it all works* ?

Here is what I came up with

```go
func TestCLI(t *testing.T) {

    t.Run("start game with 3 players and finish game with 'Chris' as winner", func(t *testing.T) {
        game := &GameSpy{}
        stdout := &bytes.Buffer{}

        in := userSends("3", "Chris wins")
        cli := poker.NewCLI(in, stdout, game)

        cli.PlayPoker()

        assertMessagesSentToUser(t, stdout, poker.PlayerPrompt)
        assertGameStartedWith(t, game, 3)
        assertFinishCalledWith(t, game, "Chris")
    })

    t.Run("start game with 8 players and record 'Cleo' as winner", func(t *testing.T) {
        game := &GameSpy{}

        in := userSends("8", "Cleo wins")
        cli := poker.NewCLI(in, dummyStdOut, game)

        cli.PlayPoker()

        assertGameStartedWith(t, game, 8)
        assertFinishCalledWith(t, game, "Cleo")
    })

    t.Run("it prints an error when a non numeric value is entered and does not start the game", func(t *testing.T) {
        game := &GameSpy{}

        stdout := &bytes.Buffer{}
        in := userSends("pies")

        cli := poker.NewCLI(in, stdout, game)
        cli.PlayPoker()

        assertGameNotStarted(t, game)
        assertMessagesSentToUser(t, stdout, poker.PlayerPrompt, poker.BadPlayerInputErrMsg)
    })
}
```

The tests now reflect the main capabilities of CLI, it is able to read user input in terms of how many people are playing and who won and handles when a bad value is entered for number of players. By doing this it is clear to the reader what `CLI` does, but also what it doesn't do.

What happens if instead of putting `Ruth wins` the user puts in `Lloyd is a killer` ?

Finish this chapter by writing a test for this scenario and making it pass.

## Wrapping up

### A quick project recap

For the past 5 chapters we have slowly TDD'd a fair amount of code

* We have two applications, a command line application and a web server.
* Both these applications rely on a `PlayerStore` to record winners
* The web server can also display a league table of who is winning the most games
* The command line app helps players play a game of poker by tracking what the current blind value is.

### time.Afterfunc

A very handy way of scheduling a function call after a specific duration. It is well worth investing time [looking at the documentation for `time`](https://golang.org/pkg/time/) as it has a lot of time saving functions and methods for you to work with.

Some of my favourites are

* `time.After(duration)` which return you a `chan Time` when the duration has expired. So if you wish to do something *after* a specific time, this can help.
* `time.NewTicker(duration)` returns a `Ticker` which is similar to the above in that it returns a channel but this one "ticks" every duration, rather than just once. This is very handy if you want to execute some code every `N duration`.

### More examples of good separation of concerns

*Generally* it is good practice to separate the responsibilities of dealing with user input and responses away from domain code. You see that here in our command line application and also our web server.

Our tests got messy. We had too many assertions (check this input, schedules these alerts, etc) and too many dependencies. We could visually see it was cluttered; it is **so important to listen to your tests**.

* If your tests look messy try and refactor them.
* If you've done this and they're still a mess it is very likely pointing to a flaw in your design
* This is one of the real strengths of tests.

Even though the tests and the production code was a bit cluttered we could freely refactor backed by our tests.

Remember when you get in to these situations to always take small steps and re-run the tests after every change.

It would've been dangerous to refactor both the test code *and* the production code at the same time, so we first refactored the production code (in the current state we couldn't improve the tests much) without changing its interface so we could rely on our tests as much as we could while changing things. *Then* we refactored the tests after the design improved.

After refactoring the dependency list reflected our design goal. This is another benefit of DI in that it often documents intent. When you rely on global variables responsibilities become very unclear.

## An example of a function implementing an interface

When you define an interface with one method in it you might want to consider defining a `MyInterfaceFunc` type to complement it so users can implement your interface with just a function

```go
type BlindAlerter interface {
    ScheduleAlertAt(duration time.Duration, amount int)
}

// BlindAlerterFunc allows you to implement BlindAlerter with a function
type BlindAlerterFunc func(duration time.Duration, amount int)

// ScheduleAlertAt is BlindAlerterFunc implementation of BlindAlerter
func (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {
    a(duration, amount)
}
```


# Websockets

[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/main/criando-uma-aplicacao/websockets)

## Recapitulando o projeto

Nós temos duas aplicações no nosso código-base de poquer.

* *Aplicação de linha de comando*. Pede ao usuário para que insira o número de jogadores. A partir daí informa os jogadores o valor da "aposta cega", que aumenta em função do tempo. A qualquer momento, um usuário pode entrar com `"{Jogador} ganhou"` para encerrar o jogo e salvar a vitória em um armazenamento.
* *Aplicação Web*. Permite que os usuários salvem os ganhadores e mostrem uma tabela da liga. Divide o armazenamento com a aplicação de linha de comando.

## Próximos passos

A dona do produto está muito contente com a aplicação por linha de comando, mas acharia melhor se conseguíssimos levar todas essas funcionalidades para o navegador. Ela imagina uma página web com uma caixa de texto que permite que o usuário coloque o número de jogadores e, após submeter esse dado, informe o valor da "aposta cega", atualizando automaticamente quando for apropriado. Assim como a aplicação por linha de comando, ela espera que o usuário possa declarar o vencedor e que isso faça com que as devidas informações sejam salvas no banco de dados.

Descrevendo o projeto dessa forma parece bastante simples, mas sempre precisamos enfatizar que devemos ter uma abordagem *iterativa* pra desenvolver os nossos programas.

Em primeiro lugar, vamos precisar apresentar um HTML. Até agora, todos os nossos *endpoints* HTTP retornaram texto puro ou JSON. Nós *poderíamos* usar as mesmas técnicas que conhecemos (porque, no fim, tanto o texto puro quanto o JSON são strings), mas nós também podemos usar o pacote [html/template](https://golang.org/pkg/html/template/) para uma solução mais limpa.

Nós também temos que ser capazes de enviar mensagens assíncronas para o usuário dizendo `A aposta blind é *y*` sem ter que recarregar o navegador. Para facilitar isso, podemos usar [WebSockets](https://pt.wikipedia.org/wiki/WebSocket).

> WebSocket é uma tecnologia que permite a comunicação bidirecional por canais full-duplex sobre um único socket TCP (Transmission Control Protocol)

Como estamos adotando várias técnicas, é ainda mais importante que façamos o menor trabalho possível primeiro e só então iteramos.

Por causa disso, a primeira coisa que faremos é criar uma página web com um formulário para o usuário salvar um vencedor. Em vez de usar um formulário simples, vamos usar os WebSockets para enviar os dados para o nosso servidor o salvar.

Depois disso, iremos trabalhaor nos alertas cegos, uma vez que já teremos algum código de infraestrutura pronto.

### E os testes para o JavaScript?

Haverá algum JavaScript escrito para cumprir nossa tarefa, mas não vamos escrever testes para ele.

É claro que é possível, mas, em nome da breviedade, não incluíremos quaisquer explicações para isso.

Desculpem, amigos. Peçam para a O'Reilly me pagar para fazer um "Aprenda JavaScript com testes".

## Escreva o teste primeiro

A primeira coisa que precisamos fazer é montar algum HTML para os usuários quando eles acessarem `/jogo`.

Aqui está um lembrete do código no nosso servidor web:

```go
type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
    http.Handler
}

const tipoConteudoJSON = "application/json"

func NovoServidorJogador(armazenamento ArmazenamentoJogador) *ServidorJogador {
    p := new(ServidorJogador)

    p.armazenamento = armazenamento

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(p.manipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(p.manipulaJogadores))

    p.Handler = roteador

    return p
}
```

A maneira *mais fácil* que podemos fazer por agora é checar que recebemos um código `200` quando acessamos o `GET /jogo`.

```go
func TestJogo(t *testing.T) {
    t.Run("GET /jogo retorna 200", func(t *testing.T) {
        servidor := NovoServidorJogador(&EsbocoDeArmazenamentoJogador{})

        requisicao, _ := http.NewRequest(http.MethodGet, "/jogo", nil)
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificaStatus(t, resposta.Code, http.StatusOK)
    })
}
```

## Tente rodar o teste

```
--- FAIL: TestJogo (0.00s)
=== RUN   TestJogo/GET_/game_returns_200
    --- FAIL: TestJogo/GET_/game_returns_200 (0.00s)
        server_test.go:109: não obteve o status correto, obtido 404, esperado 200
```

## Escreva código suficiente para fazer o teste passar

Nosso servidor tem um roteador definido, então deve ser relativamente fácil corrigir isso.

Adicione o seguinte no nosso roteador:

```go
roteador.Handle("/jogo", http.HandlerFunc(p.jogo))
```

E então escreva o método `jogo`:

```go
func (p *ServidorJogador) jogo(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
}
```

## Refatore

O servidor já está bem graças às inserções que fizemos no código já bem refatorado.

Podemos ajeitar ainda mais o teste um pouco ao adicionarmos uma função auxiliar `novaRequisicaoDeJogo` para fazer a requisição para `/jogo`. Tente escrever essa função você mesmo.

```go
func TestJogo(t *testing.T) {
    t.Run("GET /jogo retorna 200", func(t *testing.T) {
        servidor := NovoServidorJogador(&EsbocoDeArmazenamentoJogador{})

        requisicao :=  novaRequisicaoJogo()
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificaStatus(t, resposta, http.StatusOK)
    })
}
```

Você também vai notar que mudei o `verificaStatus` para aceitar `resposta` ao invés de `resposta.Code` já que parece combinar melhor.

Agora precisamos que o endpoint retorne um pouco de HTML, e aqui está ele:

```markup
<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <title>Vamos jogar pôquer</title>
</head>
<corpo>
<section id="jogo">
    <div id="declare-vencedor">
        <label for="vencedor">Vencedor</label>
        <input type="text" id="vencedor"/>
        <button id="vencedor-button">Declare vencedor</button>
    </div>
</section>
</corpo>
<script type="application/javascript">

    const submitWinnerButton = document.getElementById('vencedor-button')
    const entradaVencedor = document.getElementById('vencedor')

    if (window['WebSocket']) {
        const conexão = new WebSocket('ws://' + document.location.host + '/ws')

        submitWinnerButton.onclick = event => {
            conexão.send(entradaVencedor.value)
        }
    }
</script>
</html>
```

Temos uma página web bem simples:

* Uma entrada de texto para a pessoa inserir a vitória
* Um botão onde pode-se clicar para declarar quem venceu
* Um pouco de JavaScript para abrir uma conexão WebSocket para nosso servidor e assim gerenciar o envio dos dados ao pressionar o botão

`WebSocket` é integrado na maioria dos navegadores modernos, logo não precisamos nos preocupar em instalar bibliotecas. A página web não vai funcionar em navegadores antigos, mas para nosso caso tá tudo bem.

### Como testamos que retornamos a marcação correta?

Existem algumas formas. Como foi enfatizado no decorrer do livro, é importante que os testes que você escreve têm valor o suficiente para justificar o custo.

1. Escreva um teste baseado no navegador, usando algo como Selenium. Esses testes são os mais "realistas" de todas as abordagens porque começam um navegador web de verdade e simula um usuário interagindo com ele. Esses testes podem te dar muita confiança de que seu sistma funciona, mas são mais difíceis e escrever que os testes unitários e muito mais lentos de serem executados. Para os propósitos do nosso produto, isso é exagero.
2. Fazer uma comparação exata de textos. Isso *pode* funcionar, mas esses tipos de testes acabam sendo muito frágeis. No momento que alguém muda a marcação, você vai ter um teste falhando quando na prática nada está *de fato* falhando.
3. Verificar que chamamos o template correto. Vamos usar uma biblioteca de template da biblioteca padrão para servir o HTML (que falamos brevemente) e podemos injetar na *coisa* que gera o HTML e espionar suas chamadas para verificar que estamos fazendo tudo corretamente. Isso teria um impacto no design do nosso código, mas na realidade isso não estaríamos testando algo tão crítico além de verificar se estamos chamando o arquivo de template correto. Dito isso, só vamos ter um template no nosso projeto e a chance de falha aqui parece pequena.

Então, pela primeira vez no livro "Aprenda Go com Testes", não vamos escrever nenhum teste.

Coloque a marcação em um arquivo chamado `jogo.html`.

Na próxima mudança do endoint, vamos apenas escrever o seguinte:

```go
func (p *ServidorJogador) jogo(w http.ResponseWriter, r *http.Request) {
    tmpl, err := template.ParseFiles("jogo.html")

    if err != nil {
        http.Error(w, fmt.Sprintf("problem loading template %s", err.Error()), http.StatusInternalServerError)
        return
    }

    tmpl.Execute(w, nil)
}
```

O [`html/template`](https://golang.org/pkg/html/template/) é um pacote do Go para criar HTML. No nosso caso, chamamos `template.ParseFiles` enviando o caminho do nosso arquivo HTML. Presumindo que não há nenhum erro, chamamos a função `Execute` para "executar" o template, que o escreve para um `ìo.Writer`. No nosso caso, esperamos que o template seja escrito na internet, então enviamos o nosso `http.ResponseWriter`.

Já que não escrevemos um teste, seria prudente testar nosso servidor web manualmente só para ter certeza de que as coisas estão funcionamos como esperamos. Vá para `cmd/webserver` e execute o arquivo `main.go`. Visite `http://localhost:5000/jogo`.

Você *deve* ter obtido um erro sobre não ser capaz de encontrar o template. Você pode ou mudar o caminho para ser relativo à sua pasta, ou pode ter uma cópia de \`\`jogo.html`no diretório`cmd/webserver`. Eu escolho criar um symlink \(`ln -s ../../jogo.html jogo.html\`) para o arquivo dentro da raiz do projeto para caso eu faça alterações, elas reflitam quando o servidor estiver sendo executado.

Se fizer essa alteração e rodar novamente, deve conseguir ver a interface.

Agora precisamos testar que, quando obtemos uma string sob uma conexão WebSocket para o nosso servidor, declaramos a pessoa como vencedora de um jogo.

## Escreva o teste primeiro

Pela primeia vez, vamos usar uma biblioteca externa para trabalhar com WebSockets.

Rode `go get github.com/gorilla/websocket`.

Isso vai obter o código para a excelente biblioteca [Gorilla WebSocket](https://github.com/gorilla/websocket). Agora podemos atualizar nossos testes para nosso novo requerimento.

```go
t.Run("quando recebemos uma mensagem de um websocket que é vencedor da jogo", func(t *testing.T) {
    armazenamento := &EsbocoDeArmazenamentoJogador{}
    vencedor := "Ruth"
    servidor := httptest.NewServer(NovoServidorJogador(armazenamento))
    defer servidor.Close()

    wsURL := "ws" + strings.TrimPrefix(servidor.URL, "http") + "/ws"

    ws, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
    if err != nil {
        t.Fatalf("não foi possível abrir uma conexão de websocket em %s %v", wsURL, err)
    }
    defer ws.Close()

    if err := ws.WriteMessage(websocket.TextMessage, []byte(vencedor)); err != nil {
        t.Fatalf("não foi possível enviar mensagem na conexão websocket %v", err)
    }

    VerificaVitoriaDoVencedor(t, armazenamento, vencedor)
})
```

Certifique-se que tenha importado o pacote `websocket`. Minha IDE fez isso automaticamente para mim e a sua deve fazer o mesmo.

Para testar o que acontece do navegador, temos que abrir nossa própria conexão WebSocket e escrever nela.

Nossos testes anteriores do servidor apenas chamavam métodos no nosso servidor, mas agora precisamos ter uma conexão persistente nele. Para fazer isso, usamos o `httptest.NewServer`, que recebe um `http.Handler` que vai esperar conexões.

Ao usar `websocket.DefaultDialer.Dial`, tentamos conectar no nosso servidor para então enviar uma mensagem com nosso `vencedor`.

Por fim, verificamos o armazenamento do jogador para certificar que o vencedor foi gravado.

## Execute o teste

```
=== RUN   TestJogo/quando_recebemos_uma_mensagem_via_websocket_que_ha_um_vencedor_de_uma_jogo
    --- FAIL: TestJogo/quando_recebemos_uma_mensagem_via_websocket_que_ha_um_vencedor_de_uma_jogo (0.00s)
        server_test.go:124: não foi possível abrir uma conexão de websocket em ws://127.0.0.1:55838/ws websocket: bad handshake
```

Não mudamos nosso servidor para aceitar conexões WebSocket em `/ws`, então ainda não estamos [apertando as mãos](https://pt.wikipedia.org/wiki/Handshake).

## Escreva código suficiente para fazer o teste passar

Adicione outra linha no nosso roteador:

```go
roteador.Handle("/ws", http.HandlerFunc(p.webSocket))
```

E adicione nosso novo manipulador `webSocket`:

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    upgrader := websocket.Upgrader{
        ReadBufferSize:  1024,
        WriteBufferSize: 1024,
    }
    upgrader.Upgrade(w, r, nil)
}
```

Para aceitar uma conexão WebSocker, precisamos de um método `Upgrade` para atualizar a requisição. Agora, se você executar o teste novamente, o próximo erro deve aparecer.

```
=== RUN   TestJogo/quando_recebemos_uma_mensagem_via_websocket_que_ha_um_vencedor_de_uma_jogo
    --- FAIL: TestJogo/quando_recebemos_uma_mensagem_via_websocket_que_ha_um_vencedor_de_uma_jogo (0.00s)
        server_test.go:132: obtido 0 chamadas paraGravarVitoria esperado 1
```

Agora que temos uma conexão aberta, vamos esperar por uma pensagem e então gravá-la como vencedor.

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    upgrader := websocket.Upgrader{
        ReadBufferSize:  1024,
        WriteBufferSize: 1024,
    }
    conexão, _ := upgrader.Upgrade(w, r, nil)
    _, winnerMsg, _ := conexão.ReadMessage()
    p.armazenamento.GravarVitoria(string(winnerMsg))
}
```

(Sim, estamos ignorando vários erros nesse momento!)

`conexão.ReadMessage()` bloqueia a espera por uma mensagem na conexão. Quando obtivermos uma, vamos usá-la para `GravarVitoria`. Isso finalmente fecharia a conexão WebSocket.

Se tentar executar o teste, ele ainda vai falhar.

O problema está no tempo. Há um atraso entre nossa conexão WebSocket ler a mensagem e gravar a vitória e nosso teste termina sua execução antes disso acontecer. Você pode testar isso colocando um `time.Sleep` curto antes da verificação final.

Vamos continuar com isso por enquanto, mas saiba que colocar sleeps arbitrários em testes **é uma prática muito ruim**.

```go
time.Sleep(10 * time.Millisecond)
VerificaVitoriaDoVencedor(t, armazenamento, vencedor)
```

## Refatore

Cometemos vários pecados para fazer esse teste funcionar tanto no código do servidor quanto no código do teste, mas lembre-se que essa é a forma mais fácil para fazer as coisas funcionarem.

Temos um software horrível e cheio de gambiarras *funcionando* apoiado por testes, então agora temos a liberdade para torná-lo elegante sabendo que não vamos quebrar nada por acidente.

Então, vamos começar com o código do servidor.

Podemos mover o `upgrader` para um valor privado dentro do nosso pacote porque não precisamos redeclará-lo em toda requisição na conexão com o WebSocket.

```go
var atualizadorDeWebsocket = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    conexão, _ := atualizadorDeWebsocket.Upgrade(w, r, nil)
    _, winnerMsg, _ := conexão.ReadMessage()
    p.armazenamento.GravarVitoria(string(winnerMsg))
}
```

Nossa chamada para `template.ParseFiles("jogo.html")` vai ser executada a cada `GET /jogo`, o que significa que vamos usar o sistema de arquivo a cada requisição apesar de não ser necessário parsear o template novamente. Vamos refatorar o código para que possamos fazer o parse do template uma vez em `NovoServidorJogador` ao invés disso. Vamos ter que fazer isso para que nossa função possa retornar um erro caso tenhamos problema ao obter o template do disco ou fazer parse dele.

Agora vamos às mudanças relevantes do `ServidorJogador`:

```go
type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
    http.Handler
    template *template.Template
}

const caminhoTemplateHTML = "jogo.html"

func NovoServidorJogador(armazenamento ArmazenamentoJogador) (*ServidorJogador, error) {
    p := new(ServidorJogador)

    tmpl, err := template.ParseFiles("jogo.html")

    if err != nil {
        return nil, fmt.Errorf("problema ao abrir %s %v", caminhoTemplateHTML, err)
    }

    p.template = tmpl
    p.armazenamento = armazenamento

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(p.manipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(p.manipulaJogadores))
    roteador.Handle("/jogo", http.HandlerFunc(p.jogo))
    roteador.Handle("/ws", http.HandlerFunc(p.webSocket))

    p.Handler = roteador

    return p, nil
}

func (p *ServidorJogador) jogo(w http.ResponseWriter, r *http.Request) {
    p.template.Execute(w, nil)
}
```

Ao alterar a assinatura de `NovoServidorJogador`, agora temos problemas de compilação. Tente corrigir por si só ou olhe para o código fonte caso para ver a solução.

Para o código de teste, fiz uma função auxiliar chamada `deveFazerServidorJogador(t *testing.T, armazenamento ArmazenamentoJogador) *ServidorJogador` para que eu possa esconder o erro dos testes.

```go
func deveFazerServidorJogador(t *testing.T, armazenamento ArmazenamentoJogador) *ServidorJogador {
    servidor, err := NovoServidorJogador(armazenamento)
    if err != nil {
        t.Fatal("problema ao criar o servidor do jogador", err)
    }
    return servidor
}
```

Da mesma forma, criei outra função auxiliar `deveConectarAoWebSocket` para que eu possa esconder um erro ao criar uma conexão de WebSocket.

```go
func deveConectarAoWebSocket(t *testing.T, url string) *websocket.Conn {
    ws, _, err := websocket.DefaultDialer.Dial(url, nil)

    if err != nil {
        t.Fatalf("não foi possível abrir uma conexão de websocket em %s %v", url, err)
    }

    return ws
}
```

Finalmente, podemos criar uma função auxiliar no nosso código de teste para enviar mensagens:

```go
func escreverMensagemNoWebsocket(t *testing.T, conexão *websocket.Conn, mensagem string) {
    t.Helper()
    if err := conexão.WriteMessage(websocket.TextMessage, []byte(mensagem)); err != nil {
        t.Fatalf("não foi possível enviar mensagem na conexão websocket %v", err)
    }
}
```

Agora que os testes estão passando, tent executar o servidor e declarar alguns vencedores em `/jogo`. Devemos vê-los gravados em `/liga`. Lembre-se que sempre que tivermos um vencedor, vamos *fechar a conexão*, e você vai precisar atualizar a página para abrir a conexão novamente.

Fizemos um formulário simples da web que permite que usuários gravem o vencedor de uma jogo. Vamos iterar nele para fazer com que o usuário possa começar uma jogo inserindo o número de jogadores e o servidor vai mostrar mensagens para o cliente informando-o qual é o valor do blind conforme o tempo passa.

Primeiramente, atualize o `jogo.html` para atualizar o código do lado do cliente para os novos requerimentos:

```markup
<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <title>Vamos jogar pôquer</title>
</head>
<corpo>
<section id="jogo">
    <div id="jogo-start">
        <label for="jogador-count">Número de jogadores</label>
        <input type="number" id="jogador-count"/>
        <button id="start-jogo">Começar</button>
    </div>

    <div id="declare-vencedor">
        <label for="vencedor">Vencedor</label>
        <input type="text" id="vencedor"/>
        <button id="vencedor-button">Declare vencedor</button>
    </div>

    <div id="blind-value"/>
</section>

<section id="jogo-end">
    <h1>Outra ótima jogo de pôquer, pessoal!!</h1>
    <p><a href="/liga">Verifique a tabela da liga</a></p>
</section>

</corpo>
<script type="application/javascript">
    const startGame = document.getElementById('jogo-start')

    const declareWinner = document.getElementById('declare-vencedor')
    const submitWinnerButton = document.getElementById('vencedor-button')
    const entradaVencedor = document.getElementById('vencedor')

    const blindContainer = document.getElementById('blind-value')

    const gameContainer = document.getElementById('jogo')
    const gameEndContainer = document.getElementById('jogo-end')

    declareWinner.hidden = true
    gameEndContainer.hidden = true

    document.getElementById('start-jogo').addEventListener('click', event => {
        startGame.hidden = true
        declareWinner.hidden = false

        const numeroDeJogadores = document.getElementById('jogador-count').value

        if (window['WebSocket']) {
            const conexão = new WebSocket('ws://' + document.location.host + '/ws')

            submitWinnerButton.onclick = event => {
                conexão.send(entradaVencedor.value)
                gameEndContainer.hidden = false
                gameContainer.hidden = true
            }

            conexão.onclose = evt => {
                blindContainer.innerText = 'Connection closed'
            }

            conexão.onmessage = evt => {
                blindContainer.innerText = evt.data
            }

            conexão.onopen = function () {
                conexão.send(numeroDeJogadores)
            }
        }
    })
</script>
</html>
```

As principais alterações envolvem inserir uma seção para definir o número de jogadores e uma seção para mostrar o valor do blind. Temos um pouco de lógica para mostrar/esconder a interface do usuário dependendo da etapa da jogo.

Para qualquer mensagem que recebermos via `conexão.onmessage`, presumimos ser alertas de blind e então definimos o `blindContainer.innerText` de acordo.

Como fazemos para enviar os alertas de blind?No capítulo anterior, mostramos a ideia de `Jogo` para que nosso código CLI possa chamar um `Jogo` e todo o restante se responsabilizaria por agendar os alertas de blind. Isso acabou até sendo uma boa separação de responsabilidades.

```go
type Jogo interface {
    Começar(numeroDeJogadores int)
    Terminar(vencedor string)
}
```

Quando o usuário era requisitado pela CLI pelo número de jogadores, ele precisava `Começar` a jogo, o que ativaria os alertas de blind, e quando o usuario declarava o vencedor, isso iria `Terminar`. Esses sã os mesmos requerimentos que temos agora, só que a obtenção das entradas era diferente; logo, só precisamos reutilizar esse conceito aonde possível.

Nossa implementação "real" de `Jogo` é `TexasHoldem`:

```go
type TexasHoldem struct {
    alertador AlertadorDeBlind
    armazenamento   ArmazenamentoJogador
}
```

Ao enviar um `AlertadorDeBlind`, o `TexasHoldem` pode agendar alertas de blind para enviar para *qualquer lugar*.

```go
type AlertadorDeBlind interface {
    AgendarAlertaPara(duracao time.Duration, quantia int)
}
```

E só para lembrar, aqui está nossa implementação do `AlertadorDeBlind` que usamos na CLI.

```go
func SaidaAlertador(duracao time.Duration, quantia int) {
    time.AfterFunc(duracao, func() {
        fmt.Fprintf(os.Stdout, "Blind agora é %d\n", quantia)
    })
}
```

Isso funciona no CLI porque estamos *sempre esperando para enviar os alertas para `os.Stdout`*, mas isso não vai funcionar no nosso servidor web. Para cada requisição, obtemos um novo `http.ResponseWriter` que então melhoramos para uma `*websocket.Conn`. Logo, não odemos saber quando construímos nossas dependências para onde nossos alertas precisam ir.

Por esse motivo, precisamos mudar o `AlertadorDeBlind.AgendarAlertaPara` para que ele receba um destino paara os alertas para que possamos reutiliza-lo no nosso servidor web.

Abra o `AlertadorDeBlind.go` e adicione o parâmetro para io.Writer\`:

```go
type AlertadorDeBlind interface {
    AgendarAlertaPara(duracao time.Duration, quantia int, para io.Writer)
}

type AlertadorDeBlindFunc func(duracao time.Duration, quantia int, para io.Writer)

func (a AlertadorDeBlindFunc) AgendarAlertaPara(duracao time.Duration, quantia int, para io.Writer) {
    a(duracao, quantia, para)
}
```

A ideia de um `SaidaAlertador` não encaixa bem no nosso modelo, então vamos apenas renomeá-lo para `Alertador`:

```go
func Alertador(duracao time.Duration, quantia int, para io.Writer) {
    time.AfterFunc(duracao, func() {
        fmt.Fprintf(para, "Blind agora é %d\n", quantia)
    })
}
```

Se tentar compilar, haverá uma falha em `TexasHoldem` porque estamos chamando `AgendarAlertaPara` sem uma descrição. Só para deixar tudo compilando novamente, vamos escrevê-lo para `os.Stdout`.

Execute os testes e eles vão falhar porque o `AlertadorDeBlindEspiao` não implementa mais o `AlertadorDeBlind`. Corrija isso atualizando a assinatura de `AgendarAlertaPara`, execute os testes e todos devem estar passando.

Não faz sentido nenhum que o `TexasHoldem` saiba para onde enviar os alertas de blind. Agora, vamos atualizar o `Jogo` para que quando você começa uma jogo, declare *para onde* os alertas devem ir.

```go
type Jogo interface {
    Começar(numeroDeJogadores int, destinoDosAlertas io.Writer)
    Terminar(vencedor string)
}
```

Deixe o compilador te dizer o que precisa ser corrigido. As alterações não são tão ruins:

* Atualize o `TexasHoldem` para que implemente `Jogo` corretamente
* No `CLI`, quando começamos a jogo, preciamos passar nosssa propriedade `saida` (`cli.jogo.Começar(numeroDeJogadores, cli.saida`)
* No teste do `TexasHoldem`, precisamos usar `jogo.Começar(5, ioutil.Discard)` para corrigir o problema de compilação e configurar a saída do alerta para ser descartada

Se tiver feito tudo certo, todos os testes devem passar! Agora podemos usar `Jogo` dentro do `Servidor`.

## Escreva os testes primeiro

Os requerimentos de `CLI` e `Servidor` são os mesmos! É apenas o mecanismo de entrega que é diferente.

Vamos dar uma olhada no nosso teste do `CLI` para inspiração.

```go
t.Run("começa jogo com 3 jogadores e termina jogo com 'Chris' como vencedor", func(t *testing.T) {
    jogo := &JogoEspiao{}

    saida := &bytes.Buffer{}
    in := usuarioEnvia("3", "Chris venceu")

    poquer.NovaCLI(in, saida, jogo).JogarPoquer()

    verificaMensagensEnviadasParaUsuario(t, saida, poquer.PromptJogador)
    verificaJogoComeçadoCom(t, jogo, 3)
    verificaTerminosChamadosCom(t, jogo, "Chris")
})
```

Parece que devemos ser capazes de testar um resultado semelhante usando `JogoEspiao`.

Substitua o antigo teste de websocket com o seguinte:

```go
t.Run("começa uma jogo com 3 jogadores e declara Ruth vencedora", func(t *testing.T) {
    jogo := &poquer.JogoEspiao{}
    vencedor := "Ruth"
    servidor := httptest.NewServer(deveFazerServidorJogador(t, ArmazenamentoJogadorTosco, jogo))
    ws := deveConectarAoWebSocket(t, "ws"+strings.TrimPrefix(servidor.URL, "http")+"/ws")

    defer servidor.Close()
    defer ws.Close()

    escreverMensagemNoWebsocket(t, ws, "3")
    escreverMensagemNoWebsocket(t, ws, vencedor)

    time.Sleep(10 * time.Millisecond)
    verificaJogoComeçadoCom(t, jogo, 3)
    verificaTerminosChamadosCom(t, jogo, vencedor)
})
```

* Conforme discutidos, criamos um espião de `Jogo` e passamos para o \`\`deveFazerServidorJogador\` (certifique-se de atualizar a função auxiliar para isso).
* Depois, enviamos mensagens no web socket para uma jogo.
* Por mim, verificamos que a jogo começou e finalizamos com o que esperamos.

## Execute o teste

Você terá vários erros de compilação envolvendo `deveFazerServidorJogador` em outros testes. Crie uma variável não exportada `jogoTosco` e use-a em todos os testes que não estão compilando:

```go
var (
    jogoTosco = &JogoEspiao{}
)
```

O erro final se encontra onde estamos tentando passar em `Jogo`, pois `NovoServidorJogador` ainda não o suporta:

```
./server_test.go:21:38: too many arguments in call para "github.com/larien/aprenda-go-com-testes/WebSockets/v2".NovoServidorJogador
    have ("github.com/larien/aprenda-go-com-testes/WebSockets/v2".ArmazenamentoJogador, "github.com/larien/aprenda-go-com-testes/WebSockets/v2".Jogo)
    esperado ("github.com/larien/aprenda-go-com-testes/WebSockets/v2".ArmazenamentoJogador)
```

## Escreva o mínimo de código possível para o teste funcionar e verifique a saída do teste falhado

Basta adicionar um argumento por enquanto para fazer o teste funcionar:

```go
func NovoServidorJogador(armazenamento ArmazenamentoJogador, jogo Jogo) (*ServidorJogador, error) {
```

Finalmente!

```
=== RUN   TestJogo/começa_um_jogo_com_3_jogadores_e_declara_Ruth_a_vencedora
--- FAIL: TestJogo (0.01s)
    --- FAIL: TestJogo/começa_um_jogo_com_3_jogadores_e_declara_Ruth_a_vencedora (0.01s)
        server_test.go:146: esperava Começar chamado com 3 mas obteve 0
        server_test.go:147: esperava Terminar chamado com 'Ruth' mas obteve ''
FAIL
```

## Escreva código suficiente para fazer o teste passar

Precisamos adicionar `Jogo` como campo para `ServidorJogador` para que possamos usá-lo quando ele obtiver requisições.

```go
type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
    http.Handler
    template *template.Template
    jogo Jogo
}
```

(Já temos um método chamado `jogo`, então é só renomeá-lo para `jogarJogo`)

A seguir, vamos atribui-lo no nosso construtor:

```go
func NovoServidorJogador(armazenamento ArmazenamentoJogador, jogo Jogo) (*ServidorJogador, error) {
    p := new(ServidorJogador)

    tmpl, err := template.ParseFiles("jogo.html")

    if err != nil {
        return nil, fmt.Errorf("problema ao abrir %s %v", caminhoTemplateHTML, err)
    }

    p.jogo = jogo

    // etc
```

Agora podemos usar nosso `Jogo` dentro de `webSocket`.

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    conexão, _ := atualizadorDeWebsocket.Upgrade(w, r, nil)

    _, mensagemNumeroDeJogadores, _ := conexão.ReadMessage()
    numeroDeJogadores, _ := strconv.Atoi(string(mensagemNumeroDeJogadores))
    p.jogo.Começar(numeroDeJogadores, ioutil.Discard) //todo: Não descartar as mensagens de blind!

    _, vencedor, _ := conexão.ReadMessage()
    p.jogo.Terminar(string(vencedor))
}
```

Uhul! Os testes estão passando.

Não vamos enviar as mensagens de blind para nenhum lugar *por enquanto* já que precisamos de um tempo para pensar nisso. Quando chamamos `jogo.Começar`, enviamos os dados para `ioutil.Discard` que vai apenar descartar qualquer mensagem escrita nele.

Por enquanto, vamos iniciar o servidor. Você vai precisar atualizar a \`\`main.go`para passar um`Jogo`para o`ServidorJogador\`:

```go
func main() {
    db, err := os.OpenFile(nomeArquivoBaseDeDados, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        log.Fatalf("problema ao abrir %s %v", nomeArquivoBaseDeDados, err)
    }

    armazenamento, err := poquer.NovoSistemaArquivoArmazenamentoJogador(db)

    if err != nil {
        log.Fatalf("problema ao criar sistema de arquivo de armazenamento do jogador, %v ", err)
    }

    jogo := poquer.NovoTexasHoldem(poquer.AlertadorDeBlindFunc(poquer.Alertador), armazenamento)

    servidor, err := poquer.NovoServidorJogador(armazenamento, jogo)

    if err != nil {
        log.Fatalf("problema ao criar o servidor do jogador %v", err)
    }

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("não foi possível ouvir na porta 5000 %v", err)
    }
}
```

Tirando o fato de que não temos alertas de blind por enquanto, a aplicação funciona! Conseguimos reutilizar `Jogo` com `ServidorJogador` e ele toma conta dos detalhes. Quando descobrirmos como enviar mensagens de blind atraves de web sockets ao invés de descartá-las, tudo *deve* ficar pronto.

Antes disso, vamos mexer um pouco no código.

## Refatore

A forma que estamos usando WebSocker é bem básica e a mnnipulação de erro é bem fraca, então gostaria de encapsular isso em um tipo só para remover essa bagunça do código do servidor. Precisaremos revisitar isso depois, mas por enqaunto isso vai melhorar um pouco as coisas.

```go
type websocketServidorJogador struct {
    *websocket.Conn
}

func novoWebsocketServidorJogador(w http.ResponseWriter, r *http.Request) *websocketServidorJogador {
    conexão, err := atualizadorDeWebsocket.Upgrade(w, r, nil)

    if err != nil {
        log.Printf("houve um problema ao atualizar a conexão para WebSockets %v\n", err)
    }

    return &websocketServidorJogador{conexão}
}

func (w *websocketServidorJogador) EsperarPelaMensagem() string {
    _, msg, err := w.ReadMessage()
    if err != nil {
        log.Printf("erro ao ler do websocket %v\n", err)
    }
    return string(msg)
}
```

Agora o código do servidor fica um pouco mais simples:

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    ws := novoWebsocketServidorJogador(w, r)

    mensagemNumeroDeJogadores := ws.EsperarPelaMensagem()
    numeroDeJogadores, _ := strconv.Atoi(mensagemNumeroDeJogadores)
    p.jogo.Começar(numeroDeJogadores, ioutil.Discard) //todo: Não descartar as mensagens de blind!

    vencedor := ws.EsperarPelaMensagem()
    p.jogo.Terminar(vencedor)
}
```

Quando descobrirmos como não descartar as mensagens de blind teremos terminado essa etapa.

### *Não* vamos escrever um teste!

Às vezes, quando não temos certeza de como vamos fazer algo, é melhor apenas brincar e testar coisas diferentes! Tenha certeza de que seu trabalho está salvo primeiro porque quando descobrirmos o que fazer, vamos implementá-lo junto de um teste.

A linha problemática do código que temos é:

```go
p.jogo.Começar(numeroDeJogadores, ioutil.Discard) //todo: Não descartar as mensagens de blind!
```

Precisamos passar um `io.Writer` para a jogo para ter aonde escrever os alertas be blind.

Não seria legal se apenas precisássemos passar o nosso `websocketServidorJogador` de antes? É o nosso wrapper em torno do nosso WebSocket, então *parece* que devemos ser capazes de enviá-lo para que nosso `Jogo` seja capaz de enviar mensagens para ele.

Vamos tentar:

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    ws := novoWebsocketServidorJogador(w, r)

    mensagemNumeroDeJogadores := ws.EsperarPelaMensagem()
    numeroDeJogadores, _ := strconv.Atoi(mensagemNumeroDeJogadores)
    p.jogo.Começar(numeroDeJogadores, ws)
    //etc...
```

O compilador reclama:

```
./servidor.go:71:14: cannot use ws (type *websocketServidorJogador) as type io.Writer in argument para p.jogo.Começar:
    *websocketServidorJogador does not implement io.Writer (missing Write method)
```

Parece que a coisa óbvia a se fazer é fazer com que o `websocketServidorJogador` *implementa* o `io.Writer`. Para fazer isso, precisamos usar do `*websocket.Conn` para ussar a escrita de mensagem `WriteMessage` para enviar a mensagem para o websocket.

```go
func (w *websocketServidorJogador) Write(p []byte) (n int, err error) {
    err = w.WriteMessage(1, p)

    if err != nil {
        return 0, err
    }

    return len(p), nil
}
```

Isso parece fácil demais! Execute a aplicação para ver se funciona.

Mas antes edite o `TexasHoldem` para que o tempo de incremento do blind seja mais curto para que você possa ver as coisas em ação:

```go
incrementoDeBlind := time.Duration(5+numeroDeJogadores) * time.Second // (ao invés de um minuto)
```

As coisas devem estar funcionando! A quantidade do blind é incrementada no computador como se fosse mágica.

Agora vamos reverter o código e pensar como testá-lo. Para *implementar* isso tudo o que precisamos fazer foi passar o `websocketServidorJogador` para `ComeçarJogo` no lugar do `ioutil.Discard`, então isso faz parecer que tenhamos que espionar a chamada para verificar se ela funciona.

Espionar é ótimo e nos ajuda a verificar os detalhes de implementação, mas sempre devemos favorecer o teste do comportamento *real* se possível, porque caso seja necessário refatorar isso os testes espiões são os primeiros a começar a falhar por geralmente verificarem os detalhes de implementação que estamos tentando alterar.

Nosso teste atualmente abre uma conexão websocket para nosso servidor em execução e envia mensagens para fazê-lo efetuar ações. De forma semelhante, devemos ser capazes de testar as mensagens que o nosso servidor envia de volta para a conexão de websocket.

## Escreva o teste primeiro

Vamos editar nosso teste existente.

Atualmente, nosso `JogoEspiao` não envia nenhum dado para a `saida` quando você chama `Começar`. Devemos alterar isso para que possamos configurá-lo para enviar uma mensagem e então verificar se a mensagem é enviada para o websocket. Isso deve nos dar confiança que configuramos as coisas corretamente enquanto ainda exercitamos o comportamento real do que esperamos.

```go
type JogoEspiao struct {
    ComecouASerChamado     bool
    ComecouASerChamadoCom int
    AlertaDeBlind      []byte

    TerminouDeSerChamado   bool
    TerminouDeSerChamadoCom string
}
```

Adicione o campo de `AlertaDeBlind`.

Atualize o `Começar` do `JogoEspiao` para enviar a mensagem para a `saída`.

```go
func (j *JogoEspiao) Começar(numeroDeJogadores int, saida io.Writer) {
    j.ComecouASerChamado = true
    j.ComecouASerChamadoCom = numeroDeJogadores
    saida.Write(j.AlertaDeBlind)
}
```

Agora isso significa que quando usarmos o `ServidorJogador`, quando ele tentar `Começar` o jogo, deve acabar enviando mensagens pelo websocket se as coisas estiverem funcionando direito.

Finalmente podemos atualizar o teste:

```go
t.Run("começa uma artida com  3 jogadores, envia alguns alertas de blind no websocket e declara Ruth como vencedora", func(t *testing.T) {
    alertaDeBlindEsperado := "Blind é 100"
    vencedor := "Ruth"

    jogo := &JogoEspiao{AlertaDeBlind: []byte(alertaDeBlindEsperado)}
    servidor := httptest.NewServer(deveFazerServidorJogador(t, ArmazenamentoJogadorTosco, jogo))
    ws := deveConectarAoWebSocket(t, "ws"+strings.TrimPrefix(servidor.URL, "http")+"/ws")

    defer servidor.Close()
    defer ws.Close()

    escreverMensagemNoWebsocket(t, ws, "3")
    escreverMensagemNoWebsocket(t, ws, vencedor)

    time.Sleep(10 * time.Millisecond)
    verificaJogoComeçadoCom(t, jogo, 3)
    verificaTerminosChamadosCom(t, jogo, vencedor)

    _, alertaDeBlindObtido, _ := ws.ReadMessage()

    if string(alertaDeBlindObtido) != alertaDeBlindEsperado {
        t.Errorf("alerta de blind obtido '%s', esperado '%s'", string(alertaDeBlindObtido), alertaDeBlindEsperado)
    }
})
```

* Adicionamos um `alertaDeBlindEsperado` e configuramos nosso `JogoEspiao` para enviá-lo para a `saida` se `Começar` for chamado.
* Esperamos que ela seja enviada na conexão do websocket, então adicionamos uma chamada para `ws.ReadMessage()` para esperar por uma mensagem ser enviada e então verificamos se é aquela que esperamos.

## Execute o teste

Talvez você pense que o teste demora demais. Isso acontece porque o \`\`ws.ReadMessage()\` vai bloqueá-lo até obter a mensagem, que nunca vai chegar.

## Escreva o mínimo de código necessário para o teste ser executado e verifique a saída do teste falhando

Nunca devemos ter testes que demoram, então vamos apresentar uma nova forma de lidar com coigo que esperamos com um timeout.

```go
func within(t *testing.T, d time.Duration, assert func()) {
    t.Helper()

    done := make(chan struct{}, 1)

    go func() {
        assert()
        done <- struct{}{}
    }()

    select {
    case <-time.After(d):
        t.Error("timed out")
    case <-done:
    }
}
```

O que o `within` faz é pegar uma função `assert` como argumento e então o executa dentro de uma goroutine. Se/Quando a função termina, ela avisa que terminou através do canal `done`.

Enquanto isso acontece, usamos uma declaração `select` que nos permite esperar por um canal para enviar uma mensagem. A partir daí é uma corrida entre a função de `assert` e o `time.After` que vai enviar um sinal qunado a duração chega ao fim.

Por mim, fiz uma função auxiliar para a nossa verificação so para melhorar um pouco as coisas:

```go
func verificaSeWebSocketObteveMensagem(t *testing.T, ws *websocket.Conn, esperado string) {
    _, msg, _ := ws.ReadMessage()
    if string(msg) != esperado {
        t.Errorf(`obtido "%s", esperado "%s"`, string(msg), esperado)
    }
}
```

É assim que o teste fica agora:

```go
t.Run("começa uma artida com  3 jogadores, envia alguns alertas de blind no websocket e declara Ruth como vencedora", func(t *testing.T) {
    alertaDeBlindEsperado := "Blind é 100"
    vencedor := "Ruth"

    jogo := &JogoEspiao{AlertaDeBlind: []byte(alertaDeBlindEsperado)}
    servidor := httptest.NewServer(deveFazerServidorJogador(t, ArmazenamentoJogadorTosco, jogo))
    ws := deveConectarAoWebSocket(t, "ws"+strings.TrimPrefix(servidor.URL, "http")+"/ws")

    defer servidor.Close()
    defer ws.Close()

    escreverMensagemNoWebsocket(t, ws, "3")
    escreverMensagemNoWebsocket(t, ws, vencedor)

    time.Sleep(tenMS)

    verificaJogoComeçadoCom(t, jogo, 3)
    verificaTerminosChamadosCom(t, jogo, vencedor)
    within(t, tenMS, func() { verificaSeWebSocketObteveMensagem(t, ws, alertaDeBlindEsperado) })
})
```

Agora se você rodar o teste...

```
=== RUN   TestJogo
=== RUN   TestJogo/começa_um_jogo_com_3_jogadores,envia_alguns_alertas_de_blind_para_o_websocket_e_declara_Ruth_como_vencedora
--- FAIL: TestJogo (0.02s)
    --- FAIL: TestJogo/começa_um_jogo_com_3_jogadores,envia_alguns_alertas_de_blind_para_o_websocket_e_declara_Ruth_como_vencedora (0.02s)
        server_test.go:143: timed out
        server_test.go:150: obtido "", esperado "Blind é 100"
```

## Escreva código suficiente para fazer o teste passar

Finalmente podemos alterar o código do nosso servidor para que ele envie a mensagem para nossa conexão com o WebSocket para a jogo quando ela começa:

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    ws := novoWebsocketServidorJogador(w, r)

    mensagemNumeroDeJogadores := ws.EsperarPelaMensagem()
    numeroDeJogadores, _ := strconv.Atoi(mensagemNumeroDeJogadores)
    p.jogo.Começar(numeroDeJogadores, ws)

    vencedor := ws.EsperarPelaMensagem()
    p.jogo.Terminar(vencedor)
}
```

## Refatorar

O código do servidor sofreu uma mudança bem pequena, então não tem muito o que mudar aqui, mas o código de teste ainda tem uma chamada `time.Sleep` porque temos que esperar até que o nosso servidor termina sua tarefa assíncronamente.

Podemos refatorar nossas funções auxiliares `verificaJogoComeçadoCom` e `verificaTerminosChamadosCom` para que possam tentar as verificações novamente logo após falharem.

Abaixo esta como fazer isso com o `verificaTerminosChamadosCom` e você pode usar a mesma abordagem para a outra função auxiliar.

```go
func verificaTerminosChamadosCom(t *testing.T, jogo *JogoEspiao, vencedor string) {
    t.Helper()

    passou := tentarNovamenteAte(500*time.Millisecond, func() bool {
        return jogo.TerminouDeSerChamadoCom == vencedor
    })

    if !passou {
        t.Errorf("esperava chamada de término com '%s' mas obteve '%s' ", vencedor, jogo.TerminouDeSerChamadoCom)
    }
}
```

Aqui está como `tentarNovamenteAte` está definida:

```go
func tentarNovamenteAte(d time.Duration, f func() bool) bool {
    deadline := time.Now().Add(d)
    for time.Now().Before(deadline) {
        if f() {
            return true
        }
    }
    return false
}
```

## Resumindo

Nossa aplicação agora está completa. Um jogo de pôquer agora pode ser iniciado pelo navegador web e os usuários são informados sobre o valor da aposta cega enquanto o tempo passa por meio de WebSockets. Quando o jogo for encerrado, eles podem salvar o vencedor, o que é persistente uma vez que estamos usando o código que escrevemos há alguns capítulos atrás. Os jogadores podem descobrir quem é o melhor (ou o mais sortudo) jogador de pôquer utilizando o endpoint `/liga` do nosso website.

No decorrer da nossa jornada cometemos diversos erros, mas com o fluxo de desenvolvimento orientado a testes (TDD) nunca estivemos com um programa que não rodava de jeito nenhum. Somos livres para continuar iterando e experimentando outras coisas.

O capítulo final vai recapitular o nosso método, o design que alcançamos e por fim apertar alguns nós que possam parecer soltos.

Nós cobrimos algumas coisas nesse capítulo.

### WebSockets

* Maneira conveniente de enviar mensagens entre clientes e servidores sem precisar que o cliente fique sondando (?) o servidor. O código que fizemos tanto do cliente quanto do servidor são muito simples.
* É trivial para testar, mas você tem que se atentar com a natureza assíncrona dos testes.

### Lidando com código em testes qeu podem ter sido atrasados ou nunca terem terminado

* Crie funções utilitárias para tentar verificações novamente e adicione timeouts.
* Podemos usar go routines para certificar que as verificações não bloqueiam nada e então usar canais para deixá-los sinalizar se tiverem terminado ou não;
* O pacote `time` tem algumas funções úteis que também enviam sinais para canais sobre eventos no tempo para que possamos definir timeouts.


# OS Exec

[**You can find all the code here**](https://github.com/larien/aprenda-go-com-testes/tree/main/duvidas-da-comunidade/os-exec)

[keith6014](https://www.reddit.com/user/keith6014) asks on [reddit](https://www.reddit.com/r/golang/comments/aaz8ji/testdata_and_function_setup_help/)

> I am executing a command using os/exec.Command() which generated XML data. The command will be executed in a function called GetData().
>
> In order to test GetData(), I have some testdata which I created.
>
> In my \_test.go I have a TestGetData which calls GetData() but that will use os.exec, instead I would like for it to use my testdata.
>
> What is a good way to achieve this? When calling GetData should I have a "test" flag mode so it will read a file ie GetData(mode string)?

A few things

* When something is difficult to test, it's often due to the separation of concerns not being quite right
* Dont add "test modes" into your code, instead use [Dependency Injection](/aprenda-go-com-testes/main/primeiros-passos-com-go/injecao-de-dependencia) so that you can model your dependencies and separate concerns.

I have taken the liberty of guessing what the code might look like

```go
type Payload struct {
    Message string `xml:"message"`
}

func GetData() string {
    cmd := exec.Command("cat", "msg.xml")

    out, _ := cmd.StdoutPipe()
    var payload Payload
    decoder := xml.NewDecoder(out)

    // these 3 can return errors but I'm ignoring for brevity
    cmd.Start()
    decoder.Decode(&payload)
    cmd.Wait()

    return strings.ToUpper(payload.Message)
}
```

* It uses `exec.Command` which allows you to execute an external command to the process
* We capture the output in `cmd.StdoutPipe` which returns us a `io.ReadCloser` (this will become important)
* The rest of the code is more or less copy and pasted from the [excellent documentation](https://golang.org/pkg/os/exec/#example_Cmd_StdoutPipe).
  * We capture any output from stdout into an `io.ReadCloser` and then we `Start` the command and then wait for all the data to be read by calling `Wait`. In between those two calls we decode the data into our `Payload` struct.

Here is what is contained inside `msg.xml`

```markup
<payload>
    <message>Happy New Year!</message>
</payload>
```

I wrote a simple test to show it in action

```go
func TestGetData(t *testing.T) {
    got := GetData()
    want := "HAPPY NEW YEAR!"

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }
}
```

## Testable code

Testable code is decoupled and single purpose. To me it feels like there are two main concerns for this code

1. Retrieving the raw XML data
2. Decoding the XML data and applying our business logic (in this case `strings.ToUpper` on the `<message>`)

The first part is just copying the example from the standard lib.

The second part is where we have our business logic and by looking at the code we can see where the "seam" in our logic starts; it's where we get our `io.ReadCloser`. We can use this existing abstraction to separate concerns and make our code testable.

**The problem with GetData is the business logic is coupled with the means of getting the XML. To make our design better we need to decouple them**

Our `TestGetData` can act as our integration test between our two concerns so we'll keep hold of that to make sure it keeps working.

Here is what the newly separated code looks like

```go
type Payload struct {
    Message string `xml:"message"`
}

func GetData(data io.Reader) string {
    var payload Payload
    xml.NewDecoder(data).Decode(&payload)
    return strings.ToUpper(payload.Message)
}

func getXMLFromCommand() io.Reader {
    cmd := exec.Command("cat", "msg.xml")
    out, _ := cmd.StdoutPipe()

    cmd.Start()
    data, _ := ioutil.ReadAll(out)
    cmd.Wait()

    return bytes.NewReader(data)
}

func TestGetDataIntegration(t *testing.T) {
    got := GetData(getXMLFromCommand())
    want := "HAPPY NEW YEAR!"

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }
}
```

Now that `GetData` takes its input from just an `io.Reader` we have made it testable and it is no longer concerned how the data is retrieved; people can re-use the function with anything that returns an `io.Reader` (which is extremely common). For example we could start fetching the XML from a URL instead of the command line.

```go
func TestGetData(t *testing.T) {
    input := strings.NewReader(`
<payload>
    <message>Cats are the best animal</message>
</payload>`)

    got := GetData(input)
    want := "CATS ARE THE BEST ANIMAL"

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }
}
```

Here is an example of a unit test for `GetData`.

By separating the concerns and using existing abstractions within Go testing our important business logic is a breeze.


# Tipos de erro

[**You can find all the code here**](https://github.com/larien/aprenda-go-com-testes/tree/main/duvidas-da-comunidade/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.**

Pedro on the Gopher Slack asks

> If I’m creating an error like `fmt.Errorf("%s must be foo, got %s", bar, baz)`, is there a way to test equality without comparing the string value?

Let's make up a function to help explore this idea.

```go
// DumbGetter will get the string body of url if it gets a 200
func DumbGetter(url string) (string, error) {
    res, err := http.Get(url)

    if err != nil {
        return "", fmt.Errorf("problem fetching from %s, %v", url, err)
    }

    if res.StatusCode != http.StatusOK {
        return "", fmt.Errorf("did not get 200 from %s, got %d", url, res.StatusCode)
    }

    defer res.Body.Close()
    body, _ := ioutil.ReadAll(res.Body) // ignoring err for brevity

    return string(body), nil
}
```

It's not uncommon to write a function that might fail for different reasons and we want to make sure we handle each scenario correctly.

As Pedro says, we *could* write a test for the status error like so.

```go
t.Run("when you dont 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()

    _, err := DumbGetter(svr.URL)

    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()

    if got != want {
        t.Errorf(`got "%v", want "%v"`, got, want)
    }
})
```

This test creates a server which always returns `StatusTeapot` and then we use its URL as the argument to `DumbGetter` so we can see it handles non `200` responses correctly.

## Problems with this way of testing

This book tries to emphasise *listen to your tests* and this test doesn't *feel* good:

* We're constructing the same string as production code does to test it
* It's annoying to read and write
* Is the exact error message string what we're *actually concerned with* ?

What does this tell us? The ergonomics of our test would be reflected on another bit of code trying to use our code.

How does a user of our code react to the specific kind of errors we return? The best they can do is look at the error string which is extremely error prone and horrible to write.

## What we should do

With TDD we have the benefit of getting into the mindset of:

> How would *I* want to use this code?

What we could do for `DumbGetter` is provide a way for users to use the type system to understand what kind of error has happened.

What if `DumbGetter` could return us something like

```go
type BadStatusError struct {
    URL    string
    Status int
}
```

Rather than a magical string, we have actual *data* to work with.

Let's change our existing test to reflect this need

```go
t.Run("when you dont 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()

    _, err := DumbGetter(svr.URL)

    if err == nil {
        t.Fatal("expected an error")
    }

    got, isStatusErr := err.(BadStatusError)

    if !isStatusErr {
        t.Fatalf("was not a BadStatusError, got %T", err)
    }

    want := BadStatusError{URL:svr.URL, Status:http.StatusTeapot}

    if got != want {
        t.Errorf("got %v, want %v", got, want)
    }
})
```

We'll have to make `BadStatusError` implement the error interface.

```go
func (b BadStatusError) Error() string {
    return fmt.Sprintf("did not get 200 from %s, got %d", b.URL, b.Status)
}
```

### What does the test do?

Instead of checking the exact string of the error, we are doing a [type assertion](https://tour.golang.org/methods/15) on the error to see if it is a `BadStatusError`. This reflects our desire for the *kind* of error clearer. Assuming the assertion passes we can then check the properties of the error are correct.

When we run the test, it tells us we didn't return the right kind of error

```
--- FAIL: TestDumbGetter (0.00s)
    --- FAIL: TestDumbGetter/when_you_dont_get_a_200_you_get_a_status_error (0.00s)
        error-types_test.go:56: was not a BadStatusError, got *errors.errorString
```

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}
}
```

This change has had some *real positive effects*

* Our `DumbGetter` function has become simper, it's no longer concerned with the intricacies of an error string, it just creates a `BadStatusError`.
* Our tests now reflect (and document) what a user of our code *could* do if they decided they wanted to do some more sophisticated error handling than just logging. Just do a type assertion and then you get easy access to the properties of the error.
* It is still "just" an `error`, so if they choose to they can pass it up the call stack or log it like any other `error`.

## Wrapping up

If you find yourself testing for multiple error conditions dont fall in to the trap of comparing the error messages.

This leads to flaky and difficult to read/write tests and it reflects the difficulties the users of your code will have if they also need to start doing things differently depending on the kind of errors that have occurred.

Always make sure your tests reflect how *you'd* like to use your code, so in this respect consider creating error types to encapsulate your kinds of errors. This makes handling different kinds of errors easier for users of your code and also makes writing your error handling code simpler and easier to read.


# Por que testes unitários?

[Vejam um vídeo meu falando sobre esse assunto](https://www.youtube.com/watch?v=Kwtit8ZEK7U)

Se não gostar muito de vídeos, aqui vai o artigo relacionado a isso.

## Software

A promessa do software é que ele pode mudar. É por isso que é chamado de \_soft\_ware: é mais maleável se comparado ao hardware. Uma boa equipe de engenharia deve ser um componente incrível para uma empresa, criando sistemas que podem evoluir com um negócio para manter seu valor de entrega.

Então por que somos tão ruins nisso? Quantos projetos que você ouve falar sobre que ultrapassam o nível da falha? Ou viram "legado" e precisam ser totalmente recriados (e a reescrita também acaba falhando)!

Mas como é que um software "falha"? Não dá para ele apenas ser modificado até estar correto? É isso que prometemos!

Muita gente costuma escolher o Go para criar sistemas porque a linguagem teve várias decisões que evitam que o software vire legado.

* Comparado à minha antiga vida de Scala onde [descrevi como é fácil acabar se dando mal com a linguagem](http://www.quii.co.uk/Scala_-_Just_enough_rope_to_hang_yourself), o Go tem apenas 25 palavras-chave. *Muitos* sistemas podem ser criados a partir da biblioteca padrão e alguns outros pacotes pequenos. O que se espera é que com Go você possa escrever código, voltar a vê-lo 6 meses depois e ele ainda fazer sentido.
* As ferramentas relacionadas a testes, benchmarking, linting e shipping são incríveis se comparadas à maioria das alternativas.
* A biblioteca padrão é brilhante.
* Velocidade de compilação muito rápida para loops de feedback mais frequentes.
* A famigerada promessa da compatibilidade. Parece que Go vai receber `generics` e outras funcionalidades no futuro, mas os mantenedores prometeram que mesmo o código Go que você escreveu cinco anos atrás ainda vai compilar e funcionar. Eu literalmente passei semanas atualizando um projeto em Scala da versão 2.8 para a 2.10.

Com todas essas propriedades ótimas, ainda podemos acabar criando sistemas terríveis. Por isso, precisamos aplicar lições de engenharia de software que se aplicam independente do quão maravilhosa (ou não) sua linguagem seja.

Em 1974, um engenheiro de software esperto chamado [Manny Lehman](https://pt.wikipedia.org/wiki/Meir_M._Lehman) escreveu as [leis de Lehman para a evolução do software](https://www.baguete.com.br/colunas/jorge-horacio-audy/17/04/2014/as-8-leis-de-lehman-foram-o-manifesto-do-seculo-xx).

> As leis descrevem um equilíbrio entre o desenvolvimento de software em uma ponta e a diminuição do progresso em outra.

É importante entender esses extremos para não acabar em um ciclo infinito de entregar sistemas que se tornam em legado e precisam ser reescritos.

## Lei da Mudança Contínua

> Qualquer software utilizado no mundo real precisa se adaptar ou vai se tornar cada vez mais obsoleto.

Parece óbvio que um software *precisa* mudar ou acaba se tornando menos útil, mas quantas vezes isso é ignorado?

Muitas equipes são incentivadas a entregar um projeto em uma data específica e passar para o próximo projeto. Se o software tiver "sorte", vai acabar na mão de outro grupo de pessoas para mantê-lo, mas é claro que nenhuma dessas pessoas o escreveu.

As pessoas se preocupam em escolher um framework que vai ajudá-las a "entregar rapidamente", mas não focam na longevidade do sistema em termos de como precisa ser evoluído.

Mesmo se você for um engenheiro de software incrível, ainda vai cair na armadilha de não saber que futuro aguarda seu software. Já que o negócio muda, o código brilhante que você escreveu já não vai mais ser relevante.

Lehman estava contudo nos anos 70, porque nos deu outra lei para quebrarmos a cabeça.

## Lei da Complexidade Crescente

> Enquanto o softwate evolui, sua complexidade aumenta. A não ser que um esforço seja investido para reduzi-la.

O que ele diz aqui é que não podemos ter equipes de software para funcionar apenas como fábricas de funcionalidades, inserindo mais e mais funcionalidades no software para que ele possa sobreviver a longo prazo.

Nós **temos** que lidar com a complexidade do sistema conforme o conhecimento do nosso domínio muda.

## Refatoração

Existem *diversas* facetas na engenharia de software que mantêm um software maleável, como:

* Capacitação do desenvolvimento
* Em termos gerais, código "bom". Separação sensível de responsabilidades, etc
* Habilidades de comunicação
* Arquitetura
* Observabilidade
* Implantabilidade
* Testes automatizados
* Retornos de feedback

Vou focar na refatoração. Quantas vezes você já ouviu a frase "precisamos refatorar isso"? Provavelmente dita para uma pessoa desenvolvedora em seu primeiro dia de programação sem pensar duas vezes.

De onde essa frase vem? Por que refatorar é diferente de escrever código?

Sei que eu e muitas outras pessoas só *pensaram* que estavam refatorando, mas estávamos cometendo um erro.

[Martin Fowler descreve como as pessoas entendem a refatoração errada aqui.](https://martinfowler.com/bliki/RefactoringMalapropism.html)

> No entanto, o termo "refatoração" costuma ser utilizado de forma inapropriada. Se alguém fala que um sistema ficará quebrado por alguns dias enquanto está sendo refatorado, pode ter certeza que eles não estão refatorando.

Então o que é refatoração?

### Fatoração

Quando estudava matemática na escola, você provavelmente aprendeu fatoração. Aqui vai um exemplo bem simples:

* Calcule `1/2 + 1/4`

Para fazer isso você `fatora` os denominadores (você também pode conhecer como MMC, mínimo múltiplo comum), transformando a expressão em `2/4 + 1/4` que então pode se transformar em `3/4`.

Podemos tirar algumas lições importantes disso. Quando `fatoramos a expressão`, `não mudamos o que ela faz`. Ambas as expressões são iguais a `3/4`, mas facilitamos a forma como trabalhamos com esse resultado; trocar `1/2` por `2/4` torna nosso "domínio" mais fácil.

Quando refatora seu código, você tenta encontrar formas de tornar seu código mais fácil de entender e "encaixar" no seu entendimento atual do que o sistema precisa fazer. Mas é extremamente importante que **o comportamento do código não seja alterado**.

#### Exemplo em Go

Aqui está uma função que cumprimenta `nome` em uma `linguagem` específica:

```go
func Ola(nome, linguagem string) string {

  if linguagem == "br" {
     return "Olá, " + nome
  }

  if linguagem == "fr" {
     return "Bonjour, " + nome
  }

  // e mais várias linguagens

  return "Hello, " + nome
}
```

Não é bom ter várias condicionais `if` e temos uma duplicação que concatena um cumprimento específico da linguagem com `,` e o `nome`. Logo, vou refatorar o código.

```go
func Ola(nome, linguagem string) string {
      return fmt.Sprintf(
          "%s, %s",
          cumprimento(linguagem),
          nome,
      )
}

var cumprimentos = map[string]string{
  br: "Olá",
  fr: "Bonjour",
  // etc..
}

func cumprimento(linguagem string) string {
  cumprimento, existe := cumprimentos[linguagem]

  if existe {
     return cumprimento
  }

  return "Hello"
}
```

A natureza dessa refatoração não é tão importante. O que importa é que não mudei o comportamento do código.

Quando estiver refatorando, você pode fazer o que quiser: adicionar interfaces, tipos novos, funções, métodos etc. A única regra é que você não mude o comportamento do software.

### Quando estiver refatorando o código, seu comportamento não deve ser modificado

Isso é muito importante. Se estiver mudando o comportamento enquanto refatora, você vai estar fazendo *duas* coisas de uma vez. Como engenheiros de software, aprendemos a dividir o sistema em diferentes arquivos/pacotes/funções/etc porque sabemos que tentar entender algo enorme e acoplado é difícil.

Não queremos ter que pensar sobre muitas coisas ao mesmo tempo porque é aí que cometemos erros. Já vi tantos esforços de refatoração falharem pelas pessoas que estavam desenvolvendo darem um passo maior que a perna.

Quando fazia fatorações nas aulas de matemática com papel e caneta, eu precisava verificar manualmente que não havia mudado o significado das expressões na minha cabeça. Como sabemos que não estamos mudando o comportamento quando refatoramos as coisas no código, especialmente em um sistema que não é tão simples?

As pessoas que escolhem não escrever testes vão depender do teste manual. Para quem não trabalha em um projeto pequeno, isso vai ser uma tremenda perda de tempo e não vai escalar a longo prazo.

**Para ter uma refatoração segura, você precisa escrever testes unitários**, porque eles te dão:

* Confiança de que você pode mudar o código sem se preocupar com mudar seu comportamento
* Documentação para humanos sobre como o sistema deve se comportar
* Feedback mais rápido e confiável que o teste manual

#### Exemplo em Go

Um teste unitário para a nossa função `Ola` pode ser feito assim:

```go
func TestOla(t *testing.T) {
  obtido := Ola(“Chris”, br)
  esperado := "Olá, Chris"

  if obtido != esperado {
     t.Errorf("obtido '%s' esperado '%s'", obtido, esperado)
  }
}
```

Na linha de comando, posso executar `go test` e obter feedback imediato se minha refatoração alterou o comportamento da função. Na prática, é melhor aprender onde fica o botão mágico que vai executar seus testes dentro do seu editor/IDE (ou rodar os testes sempre que salvar o arquivo).

Você deve entrar em uma rotina em que acaba fazendo:

* Refatorar uma parte pequena
* Executar testes
* Repetir

Tudo dentro de um ciclo de feedback contínuo para que você não caia em uma cilada e cometa erros.

Ter um projeto onde os seus principais comportamentos são testados unicamente e te dão feedback em menos de um segundo traz uma relação forte de segurança para refatorar sempre que for necessário. Isso nos ajuda a gerenciar a complexidade crescente que Lehman descreve.

## Se testes unitários são tão bons, por que há resistência em escrevê-los?

De um lado, é possível ver pessoas (como eu) dizendo que testes unitários são importantes para a saúde do seu sistema a longo prazo, porque eles certificam que você possa continuar refatorando com confiança.

Do outro lado, é possível ver pessoas descrevendo experiências com testes unitários que na verdade *dificultaram* a refatoração.

Se pergunte o seguinte: com qual frequência você precisa mudar seus testes quando refatora? Estive em diversos projetos com boa cobertura de testes e mesmo assim os engenheiros estavam relutantes em refatorar por causa do esforço perceptível de alterar testes.

Esse é o oposto do que prometemos!

### Por que isso acontece?

Imagine que te pediram para desenvolver um quadrado e você chegou à conclusão que seria necessário unir dois triângulos.

![Dois triângulos retângulos formando um quadrado](https://i.imgur.com/ela7SVf.jpg)

Escrevemos nossos testes unitários nos baseando no nosso quadrado para ter certeza de que os lados são iguais e depois escrevemos alguns testes em relação aos nossos triângulos. Queremos ter certeza de que nossos triângulos são renderizados corretamente, então afirmamos que os ângulos somados dos triângulos dão 180 graus, ou verificamos que os dois são criados, etc etc. A cobertura de testes é muito importante e escrever esses testes é bem fácil, então por que não?

Algumas semanas depois, a Lei da Mudança Contínua bate no seu sistema e uma nova pessoa desenvolvedora faz algumas mudanças. Ela acredita que seria melhor se os quadrados fossem formados por dois retângulos ao invés dos dois triângulos.

![Dois retângulos formando um quadrado](https://i.imgur.com/1G6rYqD.jpg)

Ela tenta fazer essa refatoração e percebe que alguns testes falharam. Ela quebrou algum comportamento realmente importante aqui? Agora ela tem que investigar esses testes de triângulo e entender o que está acontecendo.

*Na verdade, não é tão importante que o quadrado seja formado por triângulo*, mas **nossos testes fizeram com que isso parecesse mais importante do que deveria em relação aos detalhes da nossa implementação**

## Favorecer o comportamento do teste ao invés do detalhe da implementação

Quando ouço pessoas reclamando sobre testes unitários, frequentemente o motivo é que eles estão em um nível errado de abstração. Eles testam detalhes da implementação, testando coisas muito específicas ou fazendo muitos mocks.

Acredito que isso deriva de uma falta de entendimento do que testes unitários são e perseguem métricas vaidosas (cobertura de testes).

Se estou apenas testando o comportamento, não deveríamos apenas escrever testes de sistema/caixa preta? Esses tipos de testes geram muito valor em termos de verificar as principais jornadas do usuário, mas costumam ser difíceis de escrever e lentos para rodar. Por esse motivo, eles não são muito úteis para a *refatoração* porque o ciclo de feedback é lento. Além disso, os testes de caixa preta tendem a não te ajudar muito com as causas de origem comparados aos testes unitários.

Logo, *qual* é o nível de abstração correto?

## Escrever testes unitários de forma efetiva é um problema de design

Deixando testes de lado por um momento, é desejável "unidades" independentes e desacopladas dentro do seu sistema, centradas em torno de conceitos essenciais do seu domínio.

Gosto de imaginar essas unidades tão simples quanto blocos de Lego que têm APIs coerentes e que eu possa combinar com outros blocos para criar sistemas maiores. Por baixo dessas APIs pode haver várias coisas (tipos, funções etc) colaborando para fazê-las funcionar conforme esperado.

Por exemplo: se estiver escrevendo um banco em Go, você deve ter um pacote "conta". Ele vai te apresentar uma API que não vaza detalhes da implementação e é fácil de ser integrado.

Se tiver essas unidades que seguem essas propriedades, você consegue escrever testes unitários para suas APIs públicas. *Por definição*, esses testes só podem testar os comportamentos importantes. Por baixo dos panos dessas unidades, fico livre para refatorar a implementação o quanto eu precisar e os testes para a maior parte dela não devem me atrapalhar.

### Mas são testes unitários, mesmo?

**SIM**. Testes unitários são feitos para "unidades", como já descrevi. Eles *nunca* devem ser feitos para uma classe/função/seja lá o que for.

## Conclusão

Falamos sobre

* Refatoração
* Testes unitários
* Desenvolvimento de unidade

O que podemos começar a ver é que essas facetas do desenvolvimento de software reforçam uma à outra.

### Refatoração

* Nos dá sinais sobre nossos testes unitários. Se precisamos fazer validações manuais, precisamos de mais testes. Se testes estão falhando incorretamente, então nossos testes estão no nível errado de abstração (ou não têm valor e precisam ser deletados).
* Nos ajuda a lidar com as complexidades dentro e entre nossas unidades.

### Testes unitários

* Nos dão a garantia para refatoração.
* Verificam e documentam o comportamento de nossas unidades.

### Unidades (bem definidas)

* Facilitam a escrita de testes unitários *significativos*.
* Facilitam a refatoração.

Há um processo que nos ajuda a alcançar um ponto onde podemos refatorar nosso código para lidar com a complexidade e manter nossos sistemas maleáveis?

## Por que Desenvolvimento Orientado a Testes (TDD)

Algumas pessoas levam as citações de Lehman sobre como o software deve mudar a sério demais e elaboram sistemas complexos demais, gastando muito tempo tentando prever o impossível para criar o sistema extensível "perfeito" e acabam entendendo da forma errada e chegando a lugar nenhum.

Isso vem da época das trevas do software onde um time de analistas costumava perder seis meses escrevendo um documento de requerimentos e a equipe de arquitetura perdia outros seis meses para desenvolvê-lo e alguns anos depois o projeto inteiro falhava.

Eu disse que era uma época das trevas, mas isso ainda acontece!

O movimento ágil nos ensina que precisamos trabalhar de forma iterativa, começando com pouca coisa e evoluindo o software para que tenhamos retorno rápido do design do nosso software e como ele trabalha com usuários reais; o TDD reforça essa abordagem.

O TDD aborda as leis citadas por Lehman e outras lições difíceis aprendidas no decorrer da história encorajando uma metodologia de refatoração constante e entrega contínua.

### Etapas pequenas

* Escrever um teste pequeno para uma unidade do comportamento desejado
* Verificar que o teste falha com um erro claro (vermelho)
* Escrever o mínimo de código para fazer o teste passar (verde)
* Refatorar (azul)
* Repetir

Conforme você pratica, essa mentalidade vai se tornar natural e rápida.

Você vai esperar que esse ciclo de feedback não leve muito tempo e se sentir desconfortável se estiver em um estado em que seu sistema não está "verde", já que isso pode indicar que você pode ter deixado algo passar.

Você sempre vai desenvolver de forma a criar funcionalidades pequenas & úteis confortavelmente reforçadas pelo feedback dos seus testes.

## Resumindo

* O ponto forte do software é que podemos mudá-lo. A *maioria* dos softwares requer mudança com o tempo de formas imprevisíveis; não tente pensar muito à frente porque é difícil prever o futuro.
* Ao invés disso, precisamos criar nosso software de forma que ele possa se manter maleável. Para mudar o software precisamos refatorá-lo conforme ele evolui, ou vai acabar virando uma bagunça.
* Um bom conjunto de testes pode te ajudar a refatorar mais rápido e de forma menos estressante.
* Escrever bons testes unitários é um problema de design. Logo, pense em estruturar seu código de forma que ele tenha unidades significativas que possam ser unidas como blocos de Lego.
* O TDD pode ajudar e te forçar a desenvolver softwares bem fatorados continuamente, reforçados por testes para te ajudar com futuros trabalhos que podem chegar.


# Como contribuir

Contribuições são mais que bem vindas. Espero que esse se torne um ótimo lar para guias sobre como aprender Go escrevendo testes. Você pode submeter uma PR ou criar uma issue [aqui](https://github.com/larien/aprenda-go-com-testes).

## O que estamos buscando

* Ensinar funcionalidades de Go (conceitos como `if`, `select`, estruturas, métodos etc).
* Demonstrar as funcionalidades interessantes dentro da biblioteca padrão e o quão fácil é utilizar o TDD para criar um servidor HTTP, por exemplo.
* Mostrar como o ferramental do Go, incluindo benchmarking, race detectors, etc pode te ajudar a construir um ótimo software.

Se não se sentir confiante em enviar seu próprio guia, criar uma issue para algo que queira aprender também é uma contribuição válida.

## Estilo a ser seguido

* Sempre reforce o ciclo TDD. Dê uma olhada no [Exemplo de Capítulo](/aprenda-go-com-testes/main/meta/exemplo).
* Dê ênfase em iterar sobre funcionalidades orientando-se por testes. O exemplo [Olá, mundo](/aprenda-go-com-testes/main/primeiros-passos-com-go/ola-mundo) funciona bem porque aos poucos tornamos o código mais sofisticado e aprendemos novas técnicas *orientadas por testes*. Por exemplo:
  * `Hello()` <- como escrever funções e retornar tipos.
  * `Hello(name string)` <- argumentos, constantes.
  * `Hello(name string)` <- padrão para "mundo" usando `if`.
  * `Hello(name, language string)` <- `switch`.
* Tente diminuir a barreira de conhecimento com explicações claras e simples.
  * É importante pensar em exemplos que demonstrem o que você está tentando ensinar sem confundir a leitura com outras funcionalidades.
  * Por exemplo: você pode aprender `structs` sem entender ponteiros.
  * Seja breve.
* Siga o [guia de estilo para Comentários de Revisão de Código](https://github.com/golang/go/wiki/CodeReviewComments). É importante ter um estilo consistente em todas as seções.
* Sua seção deve ter uma aplicação executável no final (como um `package main` com uma função `main`) para que as pessoas possam vê-la em ação e brincar com ela.
* Todos os testes devem passar.
* Execute o `/build.sh` antes de subir uma PR.


# Como traduzir

Requisitos: [Git](https://git-scm.com/downloads) instalado!

## Traduções x Revisões

É importante diferenciarmos quando estamos traduzindo de quando estamos revisando. As traduções acontecem diretamente relacionadas ao conteúdo original, escrito em inglês. Elas passaram por duas aprovações de outras pessoas envolvidas no projeto e já foram aceitas junto ao conteúdo oficial. As revisões acontecem quando alguma melhoria pode ser feita na tradução e passam por somente uma aprovação de outra pessoa envolvida no projeto.

## Começando a brincadeira

Na aba Projetos você pode encontrar todas as Issues criadas de acordo com cada tópico discutido no livro. Tendo selecionado um tópico para ser traduzido/revisado:

* Dê assign na issue para o seu apelido
* Clone o repositório:

```bash
git clone https://github.com/larien/aprenda-go-com-testes.git
```

* Crie uma branch seguindo o padrão `traducao/<nome-do-topico>` ou `revisao/<nome-do-topico>`:

```bash
git checkout -b traducao/<nome-do-topico>
```

* Você pode salvar a sua tradução aos poucos com `commits`:

```bash
git commit -m "Pequena descrição do que foi traduzido aqui"
```

* E não se esqueça de subir sua tradução para o repositório remoto!

```bash
git push -u origin traducao/<nome-do-topico>
```

E que comecem as traduções! :))

## Pontos para prestar atenção

* Como pode ter percebido, não é um livro formal. Logo, não é necessário ter uma linguagem rebuscada: tente usar termos e palavras acessíveis para quem está conhecendo a linguagem.
* Normalmente o artigo está relacionado a algum código presente no repositório; lembre-se de traduzir também os comentários e documentação relacionados.
* Se for um termo muito técnico, não custa nada colocar uma breve explicação do que ele é ou linkar para uma referência externa.
* Lembre-se de utilizar uma linguagem neutra! Se alguma parte do texto especificar algum gênero, reescreva-o para que inclua todo mundo.
* Se houver dúvida em alguma palavra ou tradução, coloque um asterico (\*) que o pessoal que for revisar vai tentar te ajudar.
* Quando for analisar a submissão de alguma tradução ou revisão, lembre-se de ter a gentileza em primeiro lugar! Todas as pessoas que colaborarem com o projeto querem ter um conteúdo de qualidade na nossa língua e, acima de tudo, querem aprender. Não se esqueça disso :)
* Leia o [contribuindo.md](/aprenda-go-com-testes/main/meta/contribuindo) :)

## Submetendo sua tradução/revisão

Após fazer sua tradução para a sua própria branch e subi-la para o GitHub:

* Vá na aba `Pull Requests` e clique em `New pull request`.
* Redirecione a sua branch para a branch `main` do projeto `larien/aprenda-go-com-testes`, se isso já não estiver definido.
* Na descrição, lembre-se de linkar a Issue referente ao tópico que você traduziu. Por exemplo, se o tópico que você selecionou é a Issue de número #14, digite na descrição do pull request `closes #14`. Isso vai automaticamente mover o card da sua Issue para `Done`.
* Se for uma revisão, não é necessário linkar nenhuma issue.
* Clique em `Create pull request`
* Certifique-se que sua branch passe na avaliação do Travis CI (muito daora esse carinha aí, né?)
* Aguarde a aprovação das outras pessoas envolvidas no projeto. Você receberá a notificação por e-mail quando sua PR for aprovada :) seu nome também irá para a lista de contribuições na página inicial.


# Exemplo de capítulo

Introdução

## Escreva o teste primeiro

## Execute o teste

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

## Escreva código o suficiente para fazer o teste passar

## Refatoração

## Repetir a cada novo requisito

## Resumo


# Glossário

Sinta-se livre para colaborar com esse glossário no repositório oficicial. Organizar termos do glossário em ordem alfabética. Se possível, procurar pelos termos nos textos já traduzidos e linkar com esse glossário.

## palavra

Explicação.

Encontrado em:

* [capitulo](https://github.com/larien/learn-go-with-tests/blob/main/outros/link-para-o-capitulo.md)

## palavra

Explicação.

Encontrado em:

* [capitulo](https://github.com/larien/learn-go-with-tests/blob/main/outros/link-para-o-capitulo.md)

## palavra

Explicação.

Encontrado em:

* [capitulo](https://github.com/larien/learn-go-with-tests/blob/main/outros/link-para-o-capitulo.md)


# Introdução

![](/files/-Lia9ambvvlPGkPz-Q7f)

[Arte por Denise](https://twitter.com/deniseyu21)

![Build Status](https://travis-ci.org/larien/aprenda-go-com-testes.svg?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/larien/aprenda-go-com-testes)](https://goreportcard.com/report/github.com/larien/aprenda-go-com-testes)

* Formatos: [Gitbook](https://larien.gitbook.io/aprenda-go-com-testes), [EPUB ou PDF](https://github.com/larien/aprenda-go-com-testes/releases)
* Versão original: [English](https://quii.gitbook.io/learn-go-with-tests/)

## Motivação

* Explore a linguagem Go escrevendo testes
* **Tenha uma base com TDD**. O Go é uma boa linguagem para aprender TDD por ser simples de aprender e ter testes nativamente
* Tenha confiança de que você será capaz de escrever sistemas robustos e bem testados em Go
* [Assista a um vídeo ou leia sobre o motivo pelo qual testes unitários e TDD são importantes](/aprenda-go-com-testes/meta/motivacao)

## Índice

### Primeiros Passos com Go

1. [Instalação do Go](/aprenda-go-com-testes/primeiros-passos-com-go/instalacao-do-go) - Prepare o ambiente para produtividade.
2. [Olá, mundo](/aprenda-go-com-testes/primeiros-passos-com-go/ola-mundo) - Declarando variáveis, constantes, declarações `if`/`else`, switch, escreva seu primeiro programa em Go e seu primeiro teste. Sintaxe de subteste e closures.
3. [Inteiros](/aprenda-go-com-testes/primeiros-passos-com-go/inteiros) - Mais conteúdo sobre sintaxe de declaração de função e aprenda novas formas de melhorar a documentação do seu código.
4. [Iteração](/aprenda-go-com-testes/primeiros-passos-com-go/iteracao) - Aprenda sobre `for` e benchmarking.
5. [Arrays e slices](/aprenda-go-com-testes/primeiros-passos-com-go/arrays-e-slices) - Aprenda sobre arrays, slices, `len`, variáveis recebidas como argumentos, `range` e cobertura de testes.
6. [Estruturas, métodos e interfaces](/aprenda-go-com-testes/primeiros-passos-com-go/estruturas-metodos-e-interfaces) - Aprenda sobre `structs`, métodos, `interface` e testes orientados a tabela (table driven tests).
7. [Ponteiros e erros](/aprenda-go-com-testes/primeiros-passos-com-go/ponteiros-e-erros) - Aprenda sobre ponteiros e erros.
8. [Maps](/aprenda-go-com-testes/primeiros-passos-com-go/maps) - Aprenda sobre armazenamento de valores na estrutura de dados `map`.
9. [Injeção de dependência](/aprenda-go-com-testes/primeiros-passos-com-go/injecao-de-dependencia) - Aprenda sobre injeção de dependência, qual sua relação com interfaces e uma introdução a I/O.
10. [Mocks](/aprenda-go-com-testes/primeiros-passos-com-go/mocks) - Use injeção de dependência com mocks para testar um código não testado.
11. [Concorrência](/aprenda-go-com-testes/primeiros-passos-com-go/concorrencia) - Aprenda como escrever código concorrente para tornar seu software mais rápido.
12. [Select](/aprenda-go-com-testes/primeiros-passos-com-go/select) - Aprenda a sincronizar processos assíncronos de forma elegante.
13. [Reflexão](/aprenda-go-com-testes/primeiros-passos-com-go/reflection) - Aprenda sobre reflexão.
14. [Sync](/aprenda-go-com-testes/primeiros-passos-com-go/sync) - Conheça algumas funcionalidades do pacote `sync`, como `WaitGroup` e `Mutex`.
15. [Contexto](/aprenda-go-com-testes/primeiros-passos-com-go/contexto) - Use o pacote `context` para gerenciar e cancelar processos de longa duração.

### Criando uma aplicação

Agora que você já deu seus *Primeiros Passos com Go*, esperamos que você tenha uma base sólida das principais funcionalidades da linguagem e como TDD funciona.

Essa seção envolve a criação de uma aplicação.

Cada capítulo é uma continuação do anterior, expandindo as funcionalidades da aplicação conforme nosso "Product Owner" dita.

Novos conceitos serão apresentados para ajudar a escrever código de qualidade, mas a maior parte do material novo terá relação com o que pode ser feito com a biblioteca padrão do Go.

No final desse capítulo, você deverá ter uma boa ideia de como escrever uma aplicação em Go testada.

* [Servidor HTTP](/aprenda-go-com-testes/criando-uma-aplicacao/servidor-http) - Vamos criar uma aplicação que espera por requisições HTTP e as responde.
* [JSON, routing e aninhamento](/aprenda-go-com-testes/criando-uma-aplicacao/json) - Vamos fazer nossos endpoints retornarem JSON e explorar como trabalhar com rotas.
* [IO e classificação](/aprenda-go-com-testes/criando-uma-aplicacao/io) - Vamos persistir e ler nossos dados do disco e falar sobre classificação de dados.
* [Linha de comando e estrutura do projeto](/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando) - Suportar diversas aplicações em uma base de código e ler entradas da linha de comando.
* [Tempo](/aprenda-go-com-testes/criando-uma-aplicacao/time) - Usar o pacote `time` para programar atividades.
* [Websockets](/aprenda-go-com-testes/criando-uma-aplicacao/websockets) - Aprender a escrever e testar um servidor que usa websockets.

### Dúvidas e respostas

Costumo ver perguntas nas Interwebs como:

> Como testo minha função incrível que faz x, y e z?

Se tiver esse tipo de dúvida, crie uma Issue no GitHub e vou tentar achar tempo para escrever um pequeno capítulo para resolver o problema. Acho que conteúdo como esse é valioso, já que está resolvendo problemas `reais` envolvendo testes que as pessoas têm.

* [OS exec](/aprenda-go-com-testes/perguntas-e-respostas/os-exec) - Um exemplo de como podemos usar o sistema operacional para executar comandos para buscar dados e manter nossa lógica de negócio testável.
* [Tipos de erro](/aprenda-go-com-testes/perguntas-e-respostas/error-types) - Exemplo de como criar seus próprios tipos de erro para melhorar seus testes e tornar seu código mais fácil de se trabalhar.

## Contribuição

* *Esse projeto está em desenvolvimento*, tanto seu conteúdo original quanto sua tradução. Se tiver interesse em contribuir, por favor entre em contato.
* Leia [contribuindo.md](/aprenda-go-com-testes/meta/contribuindo) para algumas diretrizes.
* Quer ajudar com a tradução para o português? Leia [traduzindo.md](/aprenda-go-com-testes/meta/traduzindo) e entenda como o processo de tradução está organizado.
* Tem ideias? Crie uma issue!

## Explicação

Tenho experiência em apresentar Go a equipes de desenvolvimento e tenho testado abordagens diferentes sobre como evoluir um grupo de pessoas que têm curiosidade sobre Go para criadores extremamente eficazes de sistemas em Go.

### O que não funcionou

#### Ler *o* livro

Uma abordagem que tentamos foi pegar [o livro azul](https://www.amazon.com.br/Linguagem-Programa%C3%A7%C3%A3o-Go-Alan-Donovan/dp/8575225464) e toda semana discutir um capítulo junto de exercícios.

Amo esse livro, mas ele exige muito comprometimento. O livro é bem detalhado na explicação de conceitos, o que obviamente é ótimo, mas significa que o progresso é lento e uniforme - não é para todo mundo.

Descobri que apenas um pequeno número de pessoas pegaria o capítulo X para ler e faria os exercícios, enquanto que a maioria não.

#### Resolver alguns problemas

Katas são divertidos, mas geralmente se limitam ao escopo de aprender uma linguagem; é improvável que você use goroutines para resolver um kata.

Outro problema é quando você tem níveis diferentes de entusiasmo. Algumas pessoas aprendem mais da linguagem que outras e, quando demonstram o que já fizeram, confundem essas pessoas apresentando funcionalidades que as outras ainda não conhecem.

Isso acaba tornando o aprendizado bem *desestruturado* e *específico*.

### O que funcionou

De longe, a forma mais eficaz foi apresentar os conceitos da linguagem aos poucos lendo o [go by example](https://gobyexample.com/), explorando-o com exemplos e discutindo-o como um grupo. Essa abordagem foi bem mais interativa do que "leia o capítulo X como lição de casa".

Com o tempo, a equipe ganhou uma base sólida da *gramática* da linguagem para que conseguíssemos começar a desenvolver sistemas.

Para mim, é semelhante à ideia de praticar escalas quando se tenta aprender a tocar violão.

Não importa quão artístico você seja; é improvável que você crie músicas boas sem entender os fundamentos e praticando os mecanismos.

### O que funcionou para mim

Quando *eu* aprendo uma nova linguagem de programação, costumo começar brincando em um REPL, mas hora ou outra preciso de mais estrutura.

O que eu gosto de fazer é explorar conceitos e então solidificar as ideias com testes. Testes certificam de que o código que escrevi está correto e documentam a funcionalidade que aprendi.

Usando minha experiência de aprendizado em grupo e a minha própria, vou tentar criar algo que seja útil para outras equipes. Aprender os conceitos escrevendo testes pequenos para que você possa usar suas habilidades de desenvolvimento de software e entregar sistemas ótimos.

## Para quem isso foi feito

* Pessoas que se interessam em aprender Go.
* Pessoas que já sabem Go, mas querem explorar testes com TDD.

## O que vamos precisar

* Um computador!
* [Go instalado](https://golang.org/)
* Um editor de texto
* Experiência com programação. Entendimento de conceitos como `if`, variáveis, funções etc.
* Se sentir confortável com o terminal

## Feedback

* Para a versão em português, submita um PR [aqui](https://github.com/larien/aprenda-go-com-testes) ou entre em contato comigo pelo [meu site](https://larien.dev).

[MIT license](https://github.com/larien/aprenda-go-com-testes/tree/09aafaeebaef4443e80a6216cc46fa3d7bfdabbb/LICENSE.md)

[Logo criado por egonelbre](https://github.com/egonelbre) Que estrela!


# Aprenda Go com Testes

![](/files/-Lia9ambvvlPGkPz-Q7f)

[Arte por Denise](https://twitter.com/deniseyu21)

* Formatos: [Gitbook](https://larien.gitbook.io/aprenda-go-com-testes), [EPUB ou PDF](https://github.com/larien/aprenda-go-com-testes/releases)
* Versão original: [English](https://quii.gitbook.io/learn-go-with-tests/)

## Motivação

* Explore a linguagem Go escrevendo testes
* **Tenha uma base com TDD**. O Go é uma boa linguagem para aprender TDD por ser simples de aprender e ter testes nativamente
* Tenha confiança de que você será capaz de escrever sistemas robustos e bem testados em Go
* [Assista a um vídeo ou leia sobre o motivo pelo qual testes unitários e TDD são importantes](/aprenda-go-com-testes/meta/motivacao)

## Explicação

Tenho experiência em apresentar Go a equipes de desenvolvimento e tenho testado abordagens diferentes sobre como evoluir um grupo de pessoas que têm curiosidade sobre Go para criadores extremamente eficazes de sistemas em Go.

### O que não funcionou

#### Ler *o* livro

Uma abordagem que tentamos foi pegar [o livro azul](https://www.amazon.com.br/Linguagem-Programa%C3%A7%C3%A3o-Go-Alan-Donovan/dp/8575225464) e toda semana discutir um capítulo junto de exercícios.

Amo esse livro, mas ele exige muito comprometimento. O livro é bem detalhado na explicação de conceitos, o que obviamente é ótimo, mas significa que o progresso é lento e uniforme - não é para todo mundo.

Descobri que apenas um pequeno número de pessoas pegaria o capítulo X para ler e faria os exercícios, enquanto que a maioria não.

#### Resolver alguns problemas

Katas são divertidos, mas geralmente se limitam ao escopo de aprender uma linguagem; é improvável que você use goroutines para resolver um kata.

Outro problema é quando você tem níveis diferentes de entusiasmo. Algumas pessoas aprendem mais da linguagem que outras e, quando demonstram o que já fizeram, confundem essas pessoas apresentando funcionalidades que as outras ainda não conhecem.

Isso acaba tornando o aprendizado bem *desestruturado* e *específico*.

### O que funcionou

De longe, a forma mais eficaz foi apresentar os conceitos da linguagem aos poucos lendo o [go by example](https://gobyexample.com/), explorando-o com exemplos e discutindo-o como um grupo. Essa abordagem foi bem mais interativa do que "leia o capítulo X como lição de casa".

Com o tempo, a equipe ganhou uma base sólida da *gramática* da linguagem para que conseguíssemos começar a desenvolver sistemas.

Para mim, é semelhante à ideia de praticar escalas quando se tenta aprender a tocar violão.

Não importa quão artístico você seja; é improvável que você crie músicas boas sem entender os fundamentos e praticando os mecanismos.

### O que funcionou para mim

Quando *eu* aprendo uma nova linguagem de programação, costumo começar brincando em um REPL, mas hora ou outra preciso de mais estrutura.

O que eu gosto de fazer é explorar conceitos e então solidificar as ideias com testes. Testes certificam de que o código que escrevi está correto e documentam a funcionalidade que aprendi.

Usando minha experiência de aprendizado em grupo e a minha própria, vou tentar criar algo que seja útil para outras equipes. Aprender os conceitos escrevendo testes pequenos para que você possa usar suas habilidades de desenvolvimento de software e entregar sistemas ótimos.

## Para quem isso foi feito

* Pessoas que se interessam em aprender Go.
* Pessoas que já sabem Go, mas querem explorar testes com TDD.

## O que vamos precisar

* Um computador!
* [Go instalado](https://golang.org/)
* Um editor de texto
* Experiência com programação. Entendimento de conceitos como `if`, variáveis, funções etc.
* Se sentir confortável com o terminal

## Feedback

* Crie issues/submita PRs [aqui](https://github.com/quii/learn-go-with-tests) ou [me envie um tweet em @quii](https://twitter.com/quii).
* Para a versão em português, submita um PR [aqui](https://github.com/larien/aprenda-go-com-testes) ou entre em contato comigo pelo [meu site](https://larien.dev).

[MIT license](https://github.com/larien/aprenda-go-com-testes/tree/35d0dbb8c6467128a4b4637742b4c2fe563c0a88/LICENSE.md)

[Logo criado por egonelbre](https://github.com/egonelbre) Que estrela!


# Instalação do Go

As instruções oficiais de instalação do Go estão disponíveis [aqui](http://www.golangbr.org/doc/instalacao).

Esse guia vai presumir que você está usando um gerenciador de pacotes como [Homebrew](https://brew.sh), [Chocolatey](https://chocolatey.org), [Apt](https://help.ubuntu.com/community/AptGet/Howto) ou [yum](https://access.redhat.com/solutions/9934).

Para propósitos de demonstração, vamos te mostrar o procedimento de instalação para o OSX usando Homebrew.

## Instalação

### Mac OSX

O processo de instalação é bem simples. Primeiro, o que você precisa fazer é executar o comando abaixo pra instalar o homebrew (brew). O Brew depende do Xcode, então você deve se certificar de instalá-lo primeiro.

```bash
xcode-select --install
```

Depois, execute o comando a seguir para instalar o homebrew:

```bash
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

Agora você consegue instalar o Go:

```bash
brew install go
```

*Siga todas as instruções recomendadas pelo seu gerenciador de pacotes. **Nota** cada grupo de instruções varia de sistema operacional para sistema operacional.*

Você pode verificar a instalação com:

```bash
$ go version
go version go1.10 darwin/amd64
```

### Linux

O processo de instalação é bem simples. Primeiro você precisa escolher e baixar a versão do Go que você deseja instalar. Para isso [acesse o site oficial](https://golang.org/) da linguagem e copie o da versão desejada (Ex.: <https://dl.google.com/go/go1.13.linux-amd64.tar.gz>). Recomendamos instalar sempre a versão mais atual.

Para baixá-lo execute o seguinte comando no seu terminal.

```bash
# selecione a versão  que você deseja instalar, no nosso exemplo estamos utilizando a versão 1.13
VERSAO_GO=1.13
cd ~
curl -O "https://dl.google.com/go/go${VERSAO_GO}.linux-amd64.tar.gz"
```

Agora descompacte os arquivos com o seguinte comando.

```bash
tar xvf "go${VERSAO_GO}.linux-amd64.tar.gz"
```

E em seguida, mova os arquivos para o diretório de binário do seu usuário.

```bash
sudo mv go /usr/local
```

Agora teste a sua instalação.

```bash
go version
go version go1.13 linux/amd64
```

### Windows

Para usuários de Windows existem duas formas de instalação, através de um arquivo ZIP que requer que você configure algumas variáveis de ambiente ou uma arquivo MSI que faz toda a configuração automaticamente. Primeiro faça download da versão que você deseja instalar. Para isso [acesse o site oficial](https://golang.org/) da linguagem e copie o arquivo da versão desejada (Ex.: <https://dl.google.com/go/go1.13.1.windows-amd64.msi>). Recomendamos instalar sempre a versão mais atual.

#### Instalação via MSI

Abra o arquivo MSI e siga os passos da instalação. Por padrão o instalador adiciona o Go na pasta `C:\Go`.

O instalador adiciona o caminho `C:\Go\bin` na variável de ambiente "Path" e cria a variável de usuário "GOPATH" com o caminho `C:\Users\%USER%\go`.

#### Instalação via ZIP

Extraia os arquivos do arquivo ZIP no diretório de sua preferência.

Adicione na variável de ambiente "Path" o caminho para a pasta "bin" dos arquivos de Go. Na busca do menu Iniciar, digite "Variáveis" escolha a opção "Editar as variáveis de ambiente do sistema. Na aba "Avançado" clique em "Variáveis de Ambiente", localize a variável "Path" e clique em Editar > Novo e preencha com o caminho escolhido (Ex: `C:/minha-pasta-go/bin`).

Quando ocorre alteração nas variáveis de ambiente no Windows é necessário reiniciar o sistema.

Nos próximos passos vamos configurar o ambiente Go. As [instruções abaixo](/aprenda-go-com-testes/primeiros-passos-com-go/instalacao-do-go#o-ambiente-go) valem tanto para sistema operacional OSX quanto para o Linux. O ambiente Windows pode requisitar configurações a mais e por isso é importante seguir a documentação oficial.

## O Ambiente Go

O Go divide opiniões.

Por convenção, todo o código Go é colocado dentro de apenas um workspace (pasta). Esse workspace pode estar em qualquer lugar da sua máquina. Se você não especificar, o Go vai definir o $HOME/go como workspace padrão. Ele é identificado (e modificado) pela variável de ambiente [GOPATH](https://golang.org/cmd/go/#hdr-GOPATH_environment_variable).

Você precisa definir a variável de ambiente para que possa utilizar futuramente em scripts, shells etc.

Atualize seu .bash\_profile para conter os seguintes `exports`:

```bash
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
```

*Nota* você deve abrir um novo terminal para definir essas variáveis de ambiente.

O Go presume que seu workspace contenha uma estrutura de diretórios específica.

Ele coloca seus arquivos em três diretórios: todo o código-fonte fica em `src`, os objetos dos pacotes ficam em `pkg` e os programas compilados são colocados em `bin`. É possível criar esses diretórios com o comando a seguir:

```bash
mkdir -p $GOPATH/src $GOPATH/pkg $GOPATH/bin
```

Agora você é capaz de usar o *go get* para que o `src/package/bin` seja instalado corretamente no diretório $GOPATH/xxx apropriado.

## Editor Go

A escolha de editor é bem pessoal. Você pode já ter um de sua preferência que tem suporte a Go. Se não tiver, leve em consideração um Editor como o [Visual Studio Code](https://code.visualstudio.com), que tem um suporte exceptional à linguagem.

Você pode instalá-lo com o comando a seguir:

```bash
brew cask install visual-studio-code
```

Confirme que o VS Code foi instalado corretamente executando o seguinte comando:

```bash
code .
```

O VS Code é lançado com poucos softwares habilidados. Você pode habilitar novos softwares instalando extensões. Para adicionar o suporte a Go, você deve instalar uma extensão. Existem várias disponíveis para o VS Code, mas uma excepcional é a do [Luke Hoban](https://github.com/Microsoft/vscode-go). Instale-a da forma a seguir:

```bash
code --install-extension ms-vscode.go
```

Quando abrir um arquivo Go pela primeira vez no VS Code, ele vai indicar que ferramentas de análises estão faltando. Clique no botão para instalá-las. A lista de ferramentas que são instaladas (e usadas) pelo VS Code estão disponíveis [aqui](https://github.com/Microsoft/vscode-go/wiki/Go-tools-that-the-Go-extension-depends-on).

## Debugger do Go

Uma boa opção para debugar seus programas em Go (que é integrado com o VS Code) é o Delve. Ele pode ser instalado da seguinte maneira usando `go get`:

```bash
go get -u github.com/go-delve/delve/cmd/dlv
```

## Linter do Go

Uma melhoria sob o linter padrão pode ser configurada usando o [GolangCI-Lint](https://github.com/golangci/golangci-lint).

Que pode ser instalada da seguinte forma:

```bash
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
```

## Refatoração e suas ferramentas

Uma grande ênfase nesse livro é dada na importância da refatoração.

Suas ferramentas podem te ajudar a fazer uma refatoração com maior confiança.

Você deve ter familiaridade o suficiente com seu editor para performar as ações a seguir com uma simples combinação de teclas:

* **Extrair/alinhar variável**. Ser capaz de pegar valores mágicos e dar um nome a eles vai simplificar seu código rapidamente.
* **Extrair método/função**. É crucial ser capaz de tirar uma seção do código e extrair funções/métodos.
* **Renomear**. Você deve se sentir capaz de renomear símbolos no decorrer dos arquivos com confiança.
* **go fmt**. O Go tem um formatador nativo chamado `go fmt`. Seu editor deve executar esse comando a cada vez que salvar o arquivo.
* **Executar testes**. Não precisa nem dizer que você deve ser capaz de fazer todos os pontos acima e então re-executar seus testes rapidamente para certificar que sua refatoração não quebrou nada.

Além disso, para te ajudar a trabalhar com seu código, você deve ser capaz de:

* **Verificar a assinatura da função**. Nunca tenha dúvida sobre a forma de chamar uma função em Go. Sua IDE deve descrever uma função em termos de sua documentação, seus parâmetros e o que ela retorna.
* **Ver a definição da função**. Se não tiver certeza sobre como uma função funciona, você deve ser capaz de ir para o código fonte de descobrir por si facilmente.
* **Encontrar usos de um símbolo**. Ser capaz de ver o contexto de uma função sendo chamada pode te ajudar com o processo de refatoração.

Dominar suas ferramentas vai te ajudar a concentrar no código e reduzir a troca de contexto.

## Resumindo

Nesse ponto você já deve ter o Go instalado, um editor disponível e algumas ferramentas básicas configuradas. O Go tem um ecossistema enorme de produtos feitos por outras pessoas. Identificamos alguns componentes úteis aqui, mas você pode encontrar uma lista mais completa no [Awesome Go](https://awesome-go.com).


# Olá, mundo

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/ola-mundo)

É comum o primeiro programa em uma nova linguagem ser um *Olá, mundo*.

No [capítulo anterior](/aprenda-go-com-testes/primeiros-passos-com-go/instalacao-do-go#o-ambiente-go), discutimos sobre como Go pode ser pouco flexível em relação a onde colocar seus arquivos.

Crie um diretório no seguinte caminho: `$GOPATH/src/github.com/{seu-lindo-nome-de-usuario}/ola`.

Se estiver num ambiente baseado em *unix* , seu nome de usuário do sistema operacional for "ariel" e tiver motivação em seguir as convenções do Go sobre `$GOPATH` (que é a maneira mais fácil de configurar) você pode executar `mkdir -p $GOPATH/src/github.com/ariel/ola`.

Crie um arquivo chamado `ola.go` no diretório mencionado e escreva o código abaixo. Para executá-lo, basta digitar `go run ola.go` no console.

```go
package main

import "fmt"

func main() {
    fmt.Println("Olá, mundo")
}
```

## Como isso funciona?

Quando você escreve um programa em Go, há um pacote `main` definido com uma função(`func`) `main` (principal) dentro dele. Os pacotes são maneiras de agrupar códigos escritos em Go.

A palavra reservada `func` é utilizada para que você defina uma função com um nome e um conteúdo.

Ao usar `import "fmt"`, estamos importando um pacote que contém a função `Println` que será utilizada para imprimir (escrever) um valor na tela.

## Como testar isso?

Como você testaria isso? É bom separar seu "domínio"(suas regras de negócio) do resto do mundo (efeitos colaterais). A função `fmt.Println` é um efeito colateral (que está imprimindo um valor no ***stdout** \[saída padrão do terminal]*) e a string que estamos enviando para dentro dela é nosso domínio.

Então, vamos separar essas referências para ficar mais fácil para testarmos.

```go
package main

import "fmt"

func Ola() string {
    return "Olá, mundo"
}

func main() {
    fmt.Println(Ola())
}
```

Criamos uma nova função usando `func`, mas dessa vez adicionamos outra palavra reservada `string` na sua definição. Isso significa que essa função terá como retorno uma `string` (*cadeia de caracteres*).

Agora, criaremos outro arquivo chamado `ola_test.go` onde iremos escrever um teste para a nossa função `Ola`.

```go
package main

import "testing"

func TestOla(t *testing.T) {
    resultado := Ola()
    esperado := "Olá, mundo"

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

Antes de explicar, vamos rodar o código. Execute `go test` no seu terminal. Ele deve passar! Para verificar, tente quebrar o teste de alguma forma mudando a string `esperado`.

Perceba que você não precisa usar várias frameworks (ou bibliotecas) de testes e complicar as coisas tentando instalá-las. Tudo o que você precisa está pronto na linguagem e a sintaxe é a mesma para o resto dos códigos que você irá escrever.

### Escrevendo testes

Escrever um teste é como escrever uma função, com algumas regras:

* Precisa estar em um arquivo com um nome parecido com `xxx_test.go`
* A função de teste precisa começar com a palavra `Test`
* A função de teste recebe um único argumento, que é `t *testing.T`

Por enquanto é o bastante para saber que o nosso `t` do tipo `*testing.T` é a nossa porta de entrada para a ferramenta de testes e assim você poderá utilizar o `t.Fail()` quando precisar relatar um erro.

Abordando alguns novos tópicos:

#### `if`

Instruções `if` em Go são muito parecidas com as de outras linguagens.

#### Declarando variáveis

Estamos declarando algumas variáveis com a sintaxe `nomeDaVariavel := valor`, que nos permite reutilizar alguns valores nos nossos testes de maneira legível.

#### `t.Errorf`

Estamos chamando o *método* `Errorf` em nosso `t` que irá imprimir uma mensagem e falhar o teste. O sufixo `f` no final de `Errorf` representa que podemos formatar e montar uma string com valores inseridos dentro de valores de preenchimentos `%s`. Quando fazemos um teste falhar, devemos ser bastante claros com o que aconteceu.

Iremos explorar a diferença entre métodos e funções depois.

### go doc

Outra funcionalidade importante do Go é sua documentação. Você pode ver a documentação na sua máquina rodando `godoc -http :8000`. Se acessar [localhost:8000/pkg](http://localhost:8000/pkg) no seu navegador, verá todos os pacotes instalados no seu sistema.

A vasta biblioteca padrão da linguagem tem uma documentação excelente com exemplos. Deve valer a pena dar uma olhada em <http://localhost:8000/pkg/testing/> para verificar o que está disponível para você.

### Olá, VOCÊ

Agora que temos um teste, podemos iterar sobre nosso software de maneira segura.

No último exemplo, escrevemos o teste somente *depois* do código ser escrito apenas para que você pudesse ter um exemplo de como escrever um teste e declarar uma função. A partir de agora, *escreveremos os testes primeiro*.

Nosso próximo requisito é nos deixar especificar quem recebe a saudação.

Vamos começar especificando esses requisitos em um teste. Estamos praticando TDD (Desenvolvimento Orientado a Testes) de forma bastante simples e que nos permite ter certeza que nosso teste está *testando* o que precisamos. Quando você escreve testes retroativamente existe o risco que seu teste possa continuar passando mesmo que o código não esteja funcionando como esperado.

```go
package main

import "testing"

func TestOla(t *testing.T) {
    resultado := Ola("Chris")
    esperado := "Olá, Chris"

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

Agora, rodando `go test`, deve ter aparecido um erro de compilação:

```
./ola_test.go:6:18: too many arguments in call to Ola
    have (string)
    want ()
```

```
./ola_test.go:6:18: argumentos demais na chamada para Ola
    tem (string)
    quer ()
```

Quando estiver usando uma linguagem estaticamente tipada como Go, é importante *dar atenção ao compilador*. O compilador entende como seu código deve se encaixar, não delegando essa função para você.

Neste caso, o compilador está te falando o que você precisa fazer para continuar. Temos que mudar a nossa função `Ola` para receber um argumento.

Edite a função `Ola` para que um argumento do tipo string seja aceito:

```go
func Ola(nome string) string {
    return "Olá, mundo"
}
```

Se tentar rodar seus testes novamente, seu arquivo `main.go` irá falhar durante a compilação porque você não está passando um argumento. Passe "mundo" como argumento para fazer o teste passar.

```go
func main() {
    fmt.Println(Ola("mundo"))
}
```

Agora, quando for rodar seus testes, você verá algo parecido com isso:

```
ola_test.go:10: resultado 'Olá, mundo', esperado 'Olá, Chris'
```

Finalmente temos um programa que compila, mas que não está satisfazendo os requisitos de acordo com o teste.

Vamos, então, fazer o teste passar usando o argumento `nome` e concatenar com `Olá,`

```go
func Ola(nome string) string {
    return "Olá, " + nome
}
```

Quando você rodar os testes, eles irão passar. É comum como parte do ciclo do TDD *refatorar* o nosso código agora.

### Uma nota sobre versionamento de código

Nesse ponto, se você estiver usando um versionamento de código (que você deveria estar fazendo!) eu faria um `commit` do código no estado atual. Agora, temos um software funcional suportado por um teste.

No entanto, eu *não faria* um push para a master, pois planejo refatorar em breve. É legal fazer um commit nesse ponto porque você pode se perder com a refatoração. Fazendo um commit você pode sempre voltar para a última versão funcional do seu software.

Não tem muita coisa para refatorar aqui, mas podemos introduzir outro recurso da linguagem: *constantes*.

### Constantes

Constantes podem ser definidas como o exemplo abaixo:

```go
const prefixoOlaPortugues = "Olá, "
```

Agora, podemos refatorar nosso código:

```go
const prefixoOlaPortugues = "Olá, "

func Ola(nome string) string {
    return prefixoOlaPortugues + nome
}
```

Depois da refatoração, rode novamente os seus testes para ter certeza que você não quebrou nada.

Constantes devem melhorar a performance da nossa aplicação, assim como evitar que você crie uma string `"Ola, "` para cada vez que `Ola` é chamado.

Para esclarecer, o aumento de performance é incrivelmente insignificante para esse exemplo! Mas vale a pena pensar em criar constantes para capturar o significado dos valores e, às vezes, para ajudar no desempenho.

## Olá, mundo... novamente

O próximo requisito é: quando nossa função for chamada com uma string vazia, ela precisa imprimir o valor padrão "Olá, mundo", ao invés de "Olá, ".

Começaremos escrevendo um novo teste que irá falhar

```go
func TestOla(t *testing.T) {

    t.Run("diz olá para as pessoas", func(t *testing.T) {
        resultado := Ola("Chris")
        esperado := "Olá, Chris"

        if resultado != esperado {
            t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
        }
    })

    t.Run("diz 'Olá, mundo' quando uma string vazia for passada", func(t *testing.T) {
        resultado := Ola("")
        esperado := "Olá, mundo"

        if resultado != esperado {
            t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
        }
    })
}
```

Aqui nós estamos apresentando outra ferramenta em nosso arsenal de testes, os *subtestes*. Às vezes, é útil agrupar testes em torno de uma "coisa" e, em seguida, ter *subtestes* descrevendo diferentes cenários.

O benefício dessa abordagem é que você poderá construir um código que pode ser compartilhado por outros testes.

Há um código repetido quando verificamos se a mensagem é o que esperamos.

A refatoração não vale *apenas* para o código de produção!

É importante que seus testes *sejam especificações claras* do que o código precisa fazer.

Podemos e devemos refatorar nossos testes.

```go
func TestOla(t *testing.T) {
    verificaMensagemCorreta := func(t *testing.T, resultado, esperado string) {
        t.Helper()
        if resultado != esperado {
            t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
        }
    }

    t.Run("diz olá para as pessoas", func(t *testing.T) {
        resultado := Ola("Chris")
        esperado := "Olá, Chris"
        verificaMensagemCorreta(t, resultado, esperado)
    })

    t.Run("'Mundo' como padrão para 'string' vazia", func(t *testing.T) {
        resultado := Ola("")
        esperado := "Olá, Mundo"
        verificaMensagemCorreta(t, resultado, esperado)
    })
}
```

### O que fizemos aqui?

Refatoramos nossa asserção em uma função. Isso reduz a duplicação e melhora a legibilidade de nossos testes. No Go, você pode declarar funções dentro de outras funções e atribui-las a variáveis. Você pode chamá-las, assim como as funções normais. Precisamos passar `t * testing.T` como parâmetro para que possamos dizer ao código de teste que ele falhará quando necessário.

`t.Helper ()` é necessário para dizermos ao conjunto de testes que este é um método auxiliar. Ao fazer isso, quando o teste falhar, o número da linha relatada estará em nossa chamada de função, e não dentro do nosso auxiliar de teste. Isso ajudará outros desenvolvedores a rastrear os problemas com maior facilidade. Se você ainda não entendeu, comente, faça um teste falhar e observe a saída do teste.

Agora que temos um teste bem escrito falhando, vamos corrigir o código usando um `if`.

```go
const prefixoOlaPortugues = "Olá, "

func Ola(nome string) string {
    if nome == "" {
        nome = "Mundo"
    }
    return prefixoOlaPortugues + nome
}
```

Se executarmos nossos testes, veremos que ele satisfaz o novo requisito e não quebramos acidentalmente a outra funcionalidade.

### De volta ao controle de versão

Agora estamos felizes com o código. Eu adicionaria mais um commit ao anterior para que possamos verificar o quão adorável ficou o nosso código com os testes.

### Disciplina

Vamos repassar o ciclo novamente:

* Escrever um teste
* Compilar o código sem erros
* Rodar o teste, ver o teste falhar e certificar que a mensagem de erro faz sentido
* Escrever a quantidade mínima de código para o teste passar
* Refatorar

Este ciclo pode parecer tedioso, mas se manter nesse ciclo de feedback é importante.

Ele não apenas garante que você tenha *testes relevantes*, como também ajuda a *projetar um bom software* refatorando-o com a segurança dos testes.

Ver a falha no teste é uma verificação importante porque também permite que você veja como é a mensagem de erro. Para quem programa, pode ser muito difícil trabalhar com uma base de código que, quando há falha nos testes, não dá uma ideia clara de qual é o problema.

Assegurando que seus testes sejam rápidos e configurando suas ferramentas para que a execução de testes seja simples, você pode entrar em um estado de fluxo ao escrever seu código.

Ao não escrever testes, você está comprometendo-se a verificar manualmente seu código executando o software que interrompe seu estado de fluxo, o que não economiza tempo, especialmente a longo prazo.

## Continue! Mais requisitos

Caramba, temos mais requisitos. Agora precisamos suportar um segundo parâmetro, especificando o idioma da saudação. Se for passado um idioma que não reconhecemos, use como padrão o português.

Devemos ter certeza de que podemos usar o TDD para aprimorar essa funcionalidade facilmente!

Escreva um teste para um usuário, passando espanhol. Adicione-o ao conjunto de testes existente.

```go
    t.Run("em espanhol", func(t *testing.T) {
        resultado := Ola("Elodie", "espanhol")
        esperado := "Hola, Elodie"
        verificaMensagemCorreta(t, resultado, esperado)
    })
```

Lembre-se de não trapacear! *Primeiro os testes*. Quando você tenta executar o teste, o compilador deve reclamar porque está chamando `Ola` com dois argumentos ao invés de um.

```
./ola_test.go:27:19: too many arguments in call to Ola
    have (string, string)
    want (string)
```

Acerte os problemas de compilação, adicionando um novo argumento do tipo `string` ao método `Ola`:

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }
    return prefixoOlaPortugues + nome
}
```

Quando você tentar executar o teste novamente, ele se queixará da função não ter recebido argumentos o suficiente para `Ola` nos seus outros testes em `ola.go`:

```
./ola.go:15:19: not enough arguments in call to Ola
    have (string)
    want (string, string)
```

Corrija-os passando `strings` vazia. Agora todos os seus testes devem compilar *e* passar, além do nosso novo cenário:

```
ola_test.go:29: resultado 'Olá, Elodie', esperado 'Hola, Elodie'
```

Podemos usar `if` aqui para verificar se o idioma é igual a "espanhol" e, em caso afirmativo, alterar a mensagem:

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    if idioma == "Espanhol" {
        return "Hola, " + nome
    }

    return prefixoOlaPortugues + nome
}
```

Os testes devem passar agora.

Agora é hora de *refatorar*. Você verá alguns problemas no código, sequências de caracteres "mágicas", algumas das quais são repetidas. Tente refatorar você mesmo, a cada alteração, execute novamente os testes para garantir que sua refatoração não esteja quebrando nada.

```go
const espanhol = "espanhol"
const prefixoOlaPortugues = "Olá, "
const prefixoOlaEspanhol = "Hola, "

func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    if idioma == espanhol {
        return prefixoOlaEspanhol + nome
    }

    return prefixoOlaPortugues + nome
}
```

### Francês

* Escreva um teste que verifique que quando passamos o idioma `"francês"`, obtemos `"Bonjour, "`
* Veja o teste falhar, verifique se a mensagem de erro é fácil de ler
* Faça a mínima alteração de código o suficiente para que o teste passe

Você pode ter escrito algo parecido com isso:

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    if idioma == espanhol {
        return prefixoOlaEspanhol + nome
    }

    if idioma == frances {
        return prefixoOlaFrances + nome
    }

    return prefixoOlaPortugues + nome
}
```

## `switch`

Quando você tem muitas instruções `if` verificando um valor específico, é comum usar uma instrução`switch`. Podemos usar o `switch` para refatorar o código facilitando a leitura e a sua extensão, caso desejarmos adicionar suporte a mais idiomas posteriormente.

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    prefixo := prefixoOlaPortugues

    switch idioma {
    case frances:
        prefixo = prefixoOlaFrances
    case espanhol:
        prefixo = prefixoOlaEspanhol
    }

    return prefixo + nome
}
```

Faça um teste para incluir agora uma saudação no idioma de sua escolha e você deve ver como é simples estender nossa *fantástica* função.

### Uma...última...refatoração?

Você pode achar que talvez nossa função esteja ficando um pouco grande. A refatoração mais simples para isso seria extrair algumas funcionalidades para outra função.

```go
func Ola(nome string, idioma string) string {
    if nome == "" {
        nome = "Mundo"
    }

    return prefixodeSaudacao(idioma) + nome
}

func prefixodeSaudacao(idioma string) (prefixo string) {
    switch idioma {
    case frances:
        prefixo = prefixoOlaFrances
    case espanhol:
        prefixo = prefixoOlaEspanhol
    default:
        prefixo = prefixoOlaPortugues
    }
    return
}
```

Alguns novos conceitos:

* Em nossa assinatura de função, criamos um valor de retorno chamado `(prefixo string)`.
* Isso criará uma variável chamada `prefixo` na nossa função.
  * Lhe será atribuído o valor "zero". Isso dependendo do tipo, por exemplo, para `int` será `0` e para strings será `""`.
    * Você pode retornar o que quer que esteja definido, apenas chamando `return` ao invés de `return prefixo`.
  * Isso será exibido no `go doc` para sua função, para que possa tornar a intenção do seu código mais clara.
* `default` será escolhido caso o valor recebido não corresponda a nenhuma das outras instruções `case` do `switch`.
* O nome da função começa com uma letra minúscula. As funções públicas em *Go* começam com uma letra maiúscula e as privadas, com minúsculas. Não queremos que as partes internas do nosso algoritmo sejam expostas ao mundo, portanto tornamos essa função privada.

## Resumindo

Quem imaginaria que você poderia tirar tanto proveito de um `Olá, mundo`?

Até agora você deve ter alguma compreensão de:

### Algumas das sintaxes da linguagem *Go* para:

* Escrever testes
* Declarar funções, com argumentos e tipos de retorno
* `if`, `const` e `switch`
* Declarar variáveis e constantes

### O processo TDD e *por que* as etapas são importantes

* *Escreva um teste que falhe e veja-o falhar*, para que saibamos que escrevemos um teste *relevante* para nossos requisitos e vimos que ele produz uma *descrição da falha fácil de entender*
* Escrever a menor quantidade de código para fazer o teste passar, para que saibamos que temos software funcionando
* *Em seguida*, refatorar, tendo a segurança de nossos testes para garantir que tenhamos um código bem feito e fácil de trabalhar

No nosso caso, passamos de `Ola()` para `Ola("nome")`, para `Ola ("nome"," Francês ")` em etapas pequenas e fáceis de entender.

Naturalmente, isso é trivial comparado ao software do "mundo real", mas os princípios ainda permanecem. O TDD é uma habilidade que precisa de prática para se desenvolver. No entanto, você será muito mais facilidade em escrever software sendo capaz de dividir os problemas em pedaços menores que possa testar.


# Inteiros

[**Você pode encontrar todos os códigos desse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/inteiros)

Inteiros funcionam como é de se esperar. Vamos escrever uma função de soma para testar algumas coisas. Crie um arquivo de teste chamado `adicionador_test.go` e escreva o seguinte código.

**nota**: Os arquivos-fonte de Go devem ter apenas um `package`(pacote) por diretório, verifique se os arquivos estão organizados separadamente. [Aqui tem uma boa explicação sobre isso (em inglês).](https://dave.cheney.net/2014/12/01/five-suggestions-for-setting-up-a-go-project)

## Escreva o teste primeiro

```go
package inteiros

import "testing"

func TestAdicionador(t *testing.T) {
    soma := Adiciona(2, 2)
    esperado := 4

    if soma != esperado {
        t.Errorf("esperado '%d', resultado '%d'", esperado, soma)
    }
}
```

Você deve ter notado que estamos usando `%d` como string de formatação, em vez de `%s`. Isso porque queremos que ele imprima um valor inteiro e não uma string. Observe também que não estamos mais usando o pacote `main`, em vez disso, definimos um pacote chamado `inteiros`, pois o nome sugere que ele agrupará funções para trabalhar com números inteiros, como Adiciona.

## Tente executar o teste

Execute o test com `go test`

Inspecione o erro de compilação

`./adicionador_test.go:6:9: undefined: Adiciona`

## Escreva a quantidade mínima de código para o teste rodar e verifique o erro na saída do teste

Escreva apenas o suficiente de código para satisfazer o compilador *e nada mais* - lembre-se de que queremos verificar se nossos testes falham pelo motivo certo.

```go
package inteiros

func Adiciona(x, y int) int {
    return 0
}
```

Quando você tem mais de um argumento do mesmo tipo (no nosso caso dois inteiros) ao invés de ter `(x int, y int)` você pode encurtá-lo para `(x, y int)`.

Agora execute os testes. Devemos ficar felizes que o teste esteja relatando corretamente o que está errado.

`adicionador_test.go:10: esperado '4', resultado '0'`

Você deve ter percebido que aprendemos sobre o *valor de retorno nomeado* na [última](https://larien.gitbook.io/aprenda-go-com-testes/primeiros-passos-com-go/pages/-LwtpBb6AKFYQKj-4TZm#uma...ultima...refatoracao?) seção, mas não estamos usando aqui. Ele geralmente deve ser usado quando o significado do resultado não está claro no contexto. No nosso caso, é muito claro que a função `Adiciona` irá adicionar os parâmetros. Você pode consultar [esta](https://github.com/golang/go/wiki/CodeReviewComments#named-result-parameters) wiki para mais detalhes.

## Escreva código o suficiente para fazer o teste passar

No sentido estrito de TDD, devemos escrever a *quantidade mínima de código para fazer o teste passar*. Uma pessoa pretenciosa pode fazer isso:

```go
func Adiciona(x, y int) int {
    return 4
}
```

Ah hah! Frustração mais uma vez! TDD é uma farsa, né?

Poderíamos escrever outro teste, com números diferentes para forçar o teste a falhar, mas isso parece um jogo de gato e rato.

Quando estivermos mais familiarizados com a sintaxe do Go, apresentarei uma técnica chamada Testes Baseados em Propriedade, que interromperá a irritação das pessoas e ajudará a encontrar bugs.

Por enquanto, vamos corrigi-lo corretamente:

```go
func Adiciona(x, y int) int {
    return x + y
}
```

Se você executar os testes novamente, eles devem passar.

## Refatoração

Não há muitas melhorias que possamos fazer aqui.

Anteriormente, vimos como nomear o argumento de retorno que aparece na documentação e também na maioria dos editores de código.

Isso é ótimo porque ajuda na usabilidade do código que você está escrevendo. É preferível que um usuário possa entender o uso de seu código apenas observando a assinatura de tipo e a documentação.

Você pode adicionar documentação em funções escrevendo comentários, e elas aparecerão no Go Doc como quando você olha a documentação da biblioteca padrão.

```go
// Adiciona recebe dois inteiros e retorna a soma deles
func Adiciona(x, y int) int {
    return x + y
}
```

### Exemplos

Se realmente quer ir além, você pode fazer [exemplos](https://blog.golang.org/examples). Você encontrará muitos exemplos na documentação da biblioteca padrão.

Muitas vezes, exemplos encontrados fora da base de código, como um arquivo readme, ficam desatualizados e incorretos em comparação com o código real, porque eles não são verificados.

Os exemplos de Go são executados da mesma forma que os testes, para que você possa ter certeza de que eles refletem o que o código realmente faz.

Exemplos são compilados (e opcionalmente executados) como parte do conjunto de testes de um pacote.

Como nos testes comuns, os exemplos são funções que residem nos arquivos \_test.go de um pacote. Adicione a seguinte função ExampleAdiciona no arquivo `adicionador_test.go`.

```go
func ExampleAdiciona() {
    soma := Adiciona(1, 5)
    fmt.Println(soma)
    // Output: 6
}
```

> obs: As palavras Example e Output foram mantidas em inglês para a execução correta do código.

(Se o seu editor não importar os pacotes automaticamente, a etapa de compilação irá falhar porque você não colocou o `import "fmt"` no `adicionador_test.go`. É altamente recomendável que você pesquise como ter esses tipos de erros corrigidos automaticamente em qualquer editor que esteja usando.)

Se o seu código mudar fazendo com que o exemplo não seja mais válido, você vai ter um erro de compilação.

Executando os testes do pacote, podemos ver que a função de exemplo é executada sem a necessidade de ajustes:

```bash
$ go test -v
=== RUN   TestAdicionador
--- PASS: TestAdicionador (0.00s)
=== RUN   ExampleAdiciona
--- PASS: ExampleAdiciona (0.00s)
```

Note que a função de exemplo não será executada se você remover o comentário "// Output: 6". Embora a função seja compilada, ela não será executada.

Ao adicionar este trecho de código, o exemplo aparecerá na documentação dentro do `godoc`, tornando seu código ainda mais acessível.

Para ver como isso funciona, execute `godoc -http=:6060` e navegue para `http://localhost:6060/pkg/`

Aqui você vai ver uma lista de todos os pacotes em seu `$GOPATH`. Então, supondo que tenha escrito esse código em algum lugar como `$GOPATH/src/github.com/{seu_id}`, você poderá encontrar uma documentação com seus exemplos.

Se você publicar seu código com exemplos em uma URL pública, poderá compartilhar a documentação em [godoc.org](https://godoc.org). Por exemplo, aqui está a API finalizada deste capítulo <https://godoc.org/github.com/larien/aprenda-go-com-testes/inteiros/v2>.

## Resumindo

Falamos sobre:

* Mais práticas do fluxo de trabalho de TDD
* Inteiros, adição
* Escrever melhores documentações para que os usuários do nosso código possam entender seu uso rapidamente
* Exemplos de como usar nosso código, que são verificados como parte de nossos testes


# Iteração

[**Você pode encontrar todo o código desse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/iteracao)

Para fazer coisas repetidamente em Go, você precisará do `for`. Go não possui nenhuma palavra chave do tipo `while`, `do` ou `until`. Você pode usar apenas `for`, o que é uma coisa boa!

Vamos escrever um teste para uma função que repete um caractere 5 vezes.

Não há nenhuma novidade até aqui, então tente escrever você mesmo para praticar.

## Escreva o teste primeiro

```go
package iteracao

import "testing"

func TestRepetir(t *testing.T) {
    repeticoes := Repetir("a")
    esperado := "aaaaa"

    if repeticoes != esperado {
        t.Errorf("esperado '%s' mas obteve '%s'", esperado, repeticoes)
    }
}
```

## Execute o teste

`./repetir_test.go:6:14: undefined: Repetir`

## Escreva a quantidade mínima de código para o teste rodar e verifique o erro na saída

*Mantenha a disciplina!* Você não precisa saber nada de diferente agora para fazer o teste falhar apropriadamente.

Tudo o que foi feito até agora é o suficiente para compilar, para que você possa verificar se escreveu o teste corretamente.

```go
package iteracao

func Repetir(caractere string) string {
    return ""
}
```

Não é legal saber que você já conhece o bastante em Go para escrever testes para problemas simples? Isso significa que agora você pode mexer no código de produção o quanto quiser sabendo que ele se comportará da maneira que você desejar.

`repetir_test.go:10: esperado 'aaaaa' mas obteve ''`

## Escreva código o suficiente para fazer o teste passar

A sintaxe do `for` é muito fácil de lembrar e segue a maioria das linguagens baseadas em `C`:

```go
func Repetir(caractere string) string {
    var repeticoes string
    for i := 0; i < 5; i++ {
        repeticoes = repeticoes + caractere
    }
    return repeticoes
}
```

Ao contrário de outras linguagens como `C`, `Java` ou `Javascript`, não há parênteses ao redor dos três componentes do `for`. No entanto, as chaves `{ }` são obrigatórias.

Execute o teste e ele deverá passar.

Variações adicionais do loop `for` podem ser vistas [aqui](https://gobyexample.com/for).

## Refatoração

Agora é hora de refatorarmos e apresentarmos outro operador de atribuição: o `+=`.

```go
const quantidadeRepeticoes = 5

func Repetir(caractere string) string {
    var repeticoes string
    for i := 0; i < quantidadeRepeticoes; i++ {
        repeticoes += caractere
    }
    return repeticoes
}
```

O operador adicionar & atribuir `+=` adiciona o valor que está à direita no valor que esta à esquerda e atribui o resultado ao valor da esquerda. Também funciona com outros tipos, como por exemplo, inteiros (`integer`).

### Benchmarking

Escrever [benchmarks](https://golang.org/pkg/testing/#hdr-Benchmarks) em Go é outro recurso disponível nativamente na linguagem e é tão facil quanto escrever testes.

```go
func BenchmarkRepetir(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Repetir("a")
    }
}
```

Você notará que o código é muito parecido com um teste.

O `testing.B` dará a você acesso a `b.N`.

Quando o benchmark é rodado, ele executa `b.N` vezes e mede quanto tempo leva.

A quantidade de vezes que o código é executado não deve importar para você. O framework irá determinar qual valor é "bom" para que você consiga ter resultados decentes.

Para executar o benchmark, digite `go test -bench=.` no terminal (ou se estiver executando do PowerShell do Windows, `go test-bench="."`)

```bash
goos: darwin
goarch: amd64
pkg: github.com/larien/aprenda-go-com-testes/primeiros-passos-com-go/iteracao/v4
10000000           136 ns/op
PASS
```

`136 ns/op` significa que nossa função demora cerca de 136 nanossegundos para ser executada (no meu computador). E isso é ótimo! Para chegar a esse resultado ela foi executada 10000000 (10 milhões de vezes) vezes.

*NOTA* por padrão, o benchmark é executado sequencialmente.

## Exercícios para praticar

* Altere o teste para que a função possa especificar quantas vezes o caractere deve ser repetido e então corrija o código para passar no teste.
* Escreva `ExampleRepetir` para documentar sua função.
* Veja também o pacote [strings](https://golang.org/pkg/strings). Encontre funções que você considera serem úteis e experimente-as escrevendo testes como fizemos aqui. Investir tempo aprendendo a biblioteca padrão irá te recompensar com o tempo.

## Resumindo

* Mais praticás de TDD
* Aprendemos o `for`
* Aprendemos como escrever benchmarks


# Arrays e slices

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/arrays-e-slices)

Arrays te permitem armazenar diversos elementos do mesmo tipo em uma variável em uma ordem específica.

Quando você tem um array, é muito comum ter que percorrer sobre ele. Logo, vamos usar nosso [recém adquirido conhecimento de `for`](/aprenda-go-com-testes/primeiros-passos-com-go/iteracao) para criar uma função `Soma`. `Soma` vai receber um array de números e retornar o total.

Também vamos praticar nossas habilidades em TDD.

## Escreva o teste primeiro

Em `soma_test.go`:

```go
package main

import "testing"

func TestSoma(t *testing.T) {

    numeros := [5]int{1, 2, 3, 4, 5}

    resultado := Soma(numeros)
    esperado := 15

    if esperado != resultado {
        t.Errorf("resultado %d, esperado %d, dado %v", resultado, esperado, numeros)
    }
}
```

Arrays têm uma *capacidade fixa* que é definida quando você declara a variável. Podemos inicializar um array de duas formas:

* \[N]tipo{valor1, valor2, ..., valorN}, como `numeros := [5]int{1, 2, 3, 4, 5}`
* \[...]tipo{valor1, valor2, ..., valorN}, como `numbers := [...]int{1, 2, 3, 4, 5}`

Às vezes é útil também mostrarmos as entradas da função na mensagem de erro. Para isso estamos usando o formatador `%v`, que é o formato "padrão" e funciona bem com arrays.

[Leia mais sobre formatação de strings aqui](https://golang.org/pkg/fmt/)

## Execute o teste

Ao executar `go test`, o compilador vai falhar com `./soma_test.go:10:15: undefined: Soma`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhado

Em `soma.go`:

```go
package main

func Soma(numeros [5]int) int {
    return 0
}
```

Agora seu teste deve falhar com uma *mensagem clara de erro*:

`soma_test.go:13: resultado 0, esperado 15, dado [1 2 3 4 5]`

## Escreva código o suficiente para fazer o teste passar

```go
func Soma(numeros [5]int) int {
    soma := 0
    for i := 0; i < 5; i++ {
        soma += numeros[i]
    }
    return soma
}
```

Para receber o valor de um array em uma posição específica, basta usar a sintaxe `array[índice]`. Nesse caso, estamos usando o `for` para percorrer cada posição do array (que tem 5 posições) e somar cada valor na variável `soma`.

## Refatoração

Vamos apresentar o [`range`](https://gobyexample.com/range) para nos ajudar a limpar o código:

```go
func Soma(numeros [5]int) int {
    soma := 0
    for _, numero := range numeros {
        soma += numero
    }
    return soma
}
```

O `range` permite que você percorra um array. Sempre que é chamado, retorna dois valores: o índice e o valor. Decidimos ignorar o valor índice usando `_` [*blank identifier*](https://golang.org/doc/effective_go.html#blank).

### Arrays e seus tipos

Uma propriedade interessante dos arrays é que seu tamanho é relacionado ao seu tipo. Se tentar passar um `[4]int` dentro da função que espera `[5]int`, ela não vai compilar. Elas são de tipos diferentes e é a mesma coisa que tentar passar uma `string` para uma função que espera um `int`.

Você pode estar pensando que é bastante complicado que arrays tenham tamanho fixo, não é? Só que na maioria das vezes, você provavelmente não vai usá-los!

O Go tem *slices*, em que você não define o tamanho da coleção e, graças a isso, pode ter qualquer tamanho.

O próprio requerimento será somar coleções de tamanhos variados.

## Escreva o teste primeiro

Agora vamos usar o [tipo slice](https://golang.org/doc/effective_go.html#slices) que nos permite ter coleções de qualquer tamanho. A sintaxe é bem parecida com a dos arrays e você só precisa omitir o tamanho quando declará-lo.

`meuSlice := []int{1,2,3}` ao invés de `meuArray := [3]int{1,2,3}`

```go
func TestSoma(t *testing.T) {

    t.Run("coleção de 5 números", func(t *testing.T) {
        numeros := [5]int{1, 2, 3, 4, 5}

        resultado := Soma(numeros)
        esperado := 15

        if resultado != esperado {
            t.Errorf("resultado %d, want %d, dado %v", resultado, esperado, numeros)
        }
    })

    t.Run("coleção de qualquer tamanho", func(t *testing.T) {
        numeros := []int{1, 2, 3}

        resultado := Soma(numeros)
        esperado := 6

        if resultado != esperado {
            t.Errorf("resultado %d, esperado %d, dado %v", resultado, esperado, numeros)
        }
    })

}
```

## Execute o teste

Isso não vai compilar.

`./soma_test.go:22:13: cannot use numbers (type []int) as type [5]int in argument to Soma`

`não é possível usar números (tipo []int) como tipo [5]int no argumento para Soma`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhado

Para resolver o problema, podemos:

* Alterar a API existente mudando o argumento de `Soma` para um slice ao invés de um array.Quando fazemos isso, vamos saber que podemos ter arruinado do dia de alguém, porque nosso *outro* teste não vai compilar!
* Criar uma nova função

No nosso caso, mais ninguém está usando nossa função. Logo, ao invés de ter duas funções para manter, vamos usar apenas uma.

```go
func Soma(numeros []int) int {
    soma := 0
    for _, numero := range numeros {
        soma += numero
    }
    return soma
}
```

Se tentar rodar os testes eles ainda não vão compilar. Você vai ter que alterar o primeiro teste e passar um slice ao invés de um array.

## Escreva código o suficiente para fazer o teste passar

Nesse caso, para arrumar os problemas de compilação, tudo o que precisamos fazer aqui é fazer os testes passarem!

## Refatoração

Nós já refatoramos a função `Soma` e tudo o que fizemos foi mudar os arrays para slices. Logo, não há muito o que fazer aqui. Lembre-se que não devemos abandonar nosso código de teste na etapa de refatoração e precisamos fazer alguma coisa aqui.

```go
func TestSoma(t *testing.T) {

    t.Run("coleção de 5 números", func(t *testing.T) {
        numeros := []int{1, 2, 3, 4, 5}

        resultado := Soma(numeros)
        esperado := 15

        if resultado != esperado {
            t.Errorf("resultado %d, esperado %d, dado, %v", resultado, esperado, numeros)
        }
    })

    t.Run("coleção de qualquer tamanho", func(t *testing.T) {
        numeros := []int{1, 2, 3}

        resultado := Soma(numeros)
        esperado := 6

        if resultado != esperado {
            t.Errorf("resultado %d, esperado %d, dado %v", resultado, esperado, numeros)
        }
    })

}
```

É importante questionar o valor dos seus testes. Ter o máximo de testes possível não deve ser o objetivo e sim ter o máximo de *confiança* possível na sua base de código. Ter testes demais pode se tornar um problema real e só adiciona mais peso na manutenção. **Todo teste tem um custo**.

No nosso caso, dá para perceber que ter dois testes para essa função é redundância. Se funciona para um slice de determindo tamanho, é muito provável que funciona para um slice de qualquer tamanho (dentro desse escopo).

A ferramenta de testes nativa do Go tem a funcionalidade de [cobertura de código](https://blog.golang.org/cover) que te ajuda a identificar áreas do seu código que você não cobriu. Já adianto que ter 100% de cobertura não deve ser seu objetivo; é apenas uma ferramenta para te dar uma ideia da sua cobertura. De qualquer forma, se você aplicar o TDD, é bem provável que chegue bem perto dos 100% de cobertura.

Tente executar `go test -cover` no terminal.

Você deve ver:

```bash
PASS
coverage: 100.0% of statements
```

Agora apague um dos testes e verifique a cobertura novamente.

Agora que estamos felizes com nossa função bem testada, você deve salvar seu trabalho incrível com um commit antes de partir para o próximo desafio.

Precisamos de uma nova função chamada `SomaTudo`, que vai receber uma quantidade variável de slices e devolver um novo slice contendo as somas de cada slice recebido.

Por exemplo:

`SomaTudo([]int{1,2}, []int{0,9})` deve retornar `[]int{3, 9}`

ou

`SomaTudo([]int{1,1,1})` deve retornar `[]int{3}`

## Escreva o teste primeiro

```go
func TestSomaTudo(t *testing.T) {

    resultado := SomaTudo([]int{1,2}, []int{0,9})
    esperado := []int{3, 9}

    if resultado != esperado {
        t.Errorf("resultado %v esperado %v", resultado, esperado)
    }
}
```

## Execute o teste

`./soma_test.go:23:9: undefined: SomaTudo`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhado

Precisamos definir o SomaTudo de acordo com o que nosso teste precisa.

O Go te permite escrever [*funções variádicas*](https://gobyexample.com/variadic-functions) em que a quantidade de argumentos podem variar.

```go
func SomaTudo(numerosParaSomar ...[]int) (somas []int) {
    return
}
```

Pode tentar compilar, mas nossos testes não vão funcionar!

`./soma_test.go:26:9: invalid operation: got != want (slice can only be compared to nil)`

`operação inválida: recebido != esperado (slice só pode ser comparado a nil`

O Go não te deixa usar operadores de igualdade com slices. *É possível* escrever uma função que percorre cada slice `recebido` e `esperado` e verificar seus valores, mas por praticidade podemos usar o [`reflect.DeepEqual`](https://golang.org/pkg/reflect/#DeepEqual) que é útil para verificar se *duas variáveis* são iguais.

```go
func TestSomaTudo(t *testing.T) {

    recebido := SomaTudo([]int{1,2}, []int{0,9})
    esperado := []int{3, 9}

    if !reflect.DeepEqual(recebido, esperado) {
        t.Errorf("recebido %v esperado %v", recebido, esperado)
    }
}
```

(coloque `import reflect` no topo do seu arquivo para ter acesso ao `DeepEqual`)

É importante saber que o `reflect.DeepEqual` não tem "segurança de tipos", ou seja, o código vai compilar mesmo se você tiver feito algo estranho. Para ver isso em ação, altere o teste temporariamente para:

```go
func TestSomaTudo(t *testing.T) {

    recebido := SomaTudo([]int{1,2}, []int{0,9})
    esperado := "joao"

    if !reflect.DeepEqual(recebido, esperado) {
        t.Errorf("recebido %v, esperado %v", recebido, esperado)
    }
}
```

O que fizemos aqui foi comparar um `slice` com uma `string`. Isso não faz sentido, mas o teste compila! Logo, apesar de ser uma forma simples de comparar slices (e outras coisas), você deve tomar cuidado quando for usar o `reflect.DeepEqual`.

Volte o teste da forma como estava e execute-o. Você deve ter a saída do teste com uma mensagem tipo:

`soma_test.go:30: recebido [], esperado [3 9]`

## Escreva código o suficiente para fazer o teste passar

O que precisamos fazer é percorrer as variáveis recebidas como argumento, calcular a soma com nossa função `Soma` de antes e adicioná-la ao slice que vamos retornar:

```go
func SomaTudo(numerosParaSomar ...[]int) (somas []int) {
    quantidadeDeNumeros := len(numerosParaSomar)
    somas = make([]int, quantidadeDeNumeros)

    for i, numeros := range numerosParaSomar {
        somas[i] = Soma(numeros)
    }

    return
 }
```

Muitas coisas novas para aprender!

Há uma nova forma de criar um slice. O `make` te permite criar um slice com uma capacidade inicial de `len` de `numerosParaSomar` que precisamos percorrer.

Você pode indexar slices como arrays com `meuSlice[N]` para obter seu valor ou designá-lo a um novo valor com `=`.

Agora o teste deve passar.

## Refatoração

Como mencionado, slices têm uma capacidade. Se você tiver um slice com uma capacidade de 2 e tentar fazer uma atribuição como `meuSlice[10] = 1`, vai receber um erro em *tempo de execução*.

No entanto, você pode usar a função `append`, que recebe um slice e um novo valor e retorna um novo slice com todos os itens dentro dele.

```go
func SomaTudo(numerosParaSomar ...[]int) []int {
    var somas []int
    for _, numeros := range numerosParaSomar {
        somas = append(somas, Soma(numeros))
    }

    return somas
}
```

Nessa implementação, nos preocupamos menos sobre capacidade. Começamos com um slice vazio `somas` e o anexamos ao resultado de `Soma` enquanto percorremos as variáveis recebidas como argumento.

Nosso próprio requisito é alterar o `SomaTudo` para `SomaTodoOResto`, onde agora calcula os totais de todos os "finais" de cada slice. O final de uma coleção é todos os itens com exceção do primeiro (a "cabeça").

## Escreva o teste primeiro

```go
func TestSomaTodoOResto(t *testing.T) {
    resultado := SomaTodoOResto([]int{1,2}, []int{0,9})
    esperado := []int{2, 9}

    if !reflect.DeepEqual(resultado, esperado) {
        t.Errorf("resultado %v, esperado %v", resultado, esperado)
    }
}
```

## Execute o teste

`./soma_test.go:26:9: undefined: SomaTodoOResto`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhado

Renomeie a função para `SomaTodoOResto` e volte a executar o teste.

`soma_test.go:30: resultado [3 9], esperado [2 9]`

## Escreva código o suficiente para fazer o teste passar

```go
func SomaTodoOResto(numerosParaSomar ...[]int) []int {
    var somas []int
    for _, numeros := range numerosParaSomar {
        final := numeros[1:]
        somas = append(somas, Soma(final))
    }

    return somas
}
```

Slices podem ser "fatiados"! A sintaxe usada é `slice[inicio:final]`. Se você omitir o valor de um dos lados dos `:` ele captura tudo do lado omitido. No nosso caso, quando usamos `numeros[1:]`, estamos dizendo "pegue da posição 1 até o final". É uma boa ideia investir um tempo escrevend outros testes com slices e brincar com o operador slice para criar mais familiaridade com ele.

## Refatoração

Não tem muito o que refatorar dessa vez.

O que acha que aconteceria se você passar um slice vazio para a nossa função? Qual é o "final" de um slice vazio? O que acontece quando você fala para o Go capturar todos os elementos de `meuSliceVazio[1:]`?

## Escreva o teste primeiro

```go
func TestSomaTodoOResto(t *testing.T) {

    t.Run("faz as somas de alguns slices", func(t *testing.T) {
        resultado := SomaTodoOResto([]int{1,2}, []int{0,9})
        esperado := []int{2, 9}

        if !reflect.DeepEqual(resultado, esperado) {
            t.Errorf("resultado %v, esperado %v", resultado, esperado)
        }
    })

    t.Run("soma slices vazios de forma segura", func(t *testing.T) {
        resultado := SomaTodoOResto([]int{}, []int{3, 4, 5})
        esperado := []int{0, 9}

        if !reflect.DeepEqual(resultado, esperado) {
            t.Errorf("resultado %v, esperado %v", resultado, esperado)
        }
    })

}
```

## Execute o teste

```bash
panic: runtime error: slice bounds out of range [recovered]
    panic: runtime error: slice bounds out of range
```

`pânico: erro em tempo de execução: fora da capacidade do slice`

Oh, não! É importante perceber que o test *foi compilado*, esse é um erro em tempo de execução. Erros em tempo de compilação são nossos amigos, porque nos ajudam a escrever softwares que funcionam. Erros em tempo de execução são nosso inimigos, porque afetam nossos usuários.

## Escreva código o suficiente para fazer o teste passar

```go
func SomaTodoOResto(numerosParaSomar ...[]int) []int {
    var somas []int
    for _, numeros := range numerosParaSomar {
        if len(numeros) == 0 {
            somas = append(somas, 0)
        } else {
            final := numeros[1:]
            somas = append(somas, Soma(final))
        }
    }

    return somas
}
```

## Refatoração

Nossos testes têm código repetido em relação à asserção de novo. Vamos encapsular isso em uma função:

```go
func TestSomaTodoOResto(t *testing.T) {

    verificaSomas := func(t *testing.T, resultado, esperado []int) {
        t.Helper()
        if !reflect.DeepEqual(resultado, esperado) {
            t.Errorf("resultado %v, esperado %v", resultado, esperado)
        }
    }

    t.Run("faz a soma do resto", func(t *testing.T) {
        resultado := SomaTodoOResto([]int{1, 2}, []int{0, 9})
        esperado := []int{2, 9}
        verificaSomas(t, resultado, esperado)
    })

    t.Run("soma slices vazios de forma segura", func(t *testing.T) {
        resultado := SomaTodoOResto([]int{}, []int{3, 4, 5})
        esperado := []int{0, 9}
        verificaSomas(t, resultado, esperado)
    })

}
```

Um efeito colateral útil disso é que adiciona um pouco de segurança de tipos no nosso código. Se uma pessoa espertinha adicionar um novo teste com `verificaSomas(t, resultado, "luisa")` o compilador vai pará-lo antes que algo errado aconteça.

```bash
$ go test
./soma_test.go:52:21: cannot use "luisa" (type string) as type []int in argument to verificaSomas
```

`não é possível usar "luisa" (tipo string) como tipo []int no argumento para verificaSomas`

## Resumindo

Falamos sobre:

* Arrays
* Slices
* Várias formas de criá-las
* Como eles têm uma capacidade *fixa*, mas é posível criar novos slices de antigos usando `append`
* Como "fatiar" slices!
* `len` obtém o tamanho de um array ou slice
* Ferramenta de cobertura de testes
* `reflect.DeepEqual` e por que é útil, mas pode diminuir a segurança de tipos do seu código

Usamos slices e arrays com inteiros, mas eles também funcionam com qualquer outro tipo, incluindo até os próprios arrays/slices. Logo, você pode declarar uma variável de `[][]string` se precisar.

[Dê uma olhada no post sobre slices no blog de Go](https://blog.golang.org/go-slices-usage-and-internals) para saber mais sobre slices. Tente escrever mais testes para demonstrar o que você aprendeu com a leitura.

Outra forma útil de brincar com Go ao invés de escrever testes é o Go playground. Você pode testar mais coisas lá e você pode compartilhar seu código facilmente se precisar tirar dúvidas. [Criei um exemplo com um slice para testar lá.](https://play.golang.org/p/ICCWcRGIO68)


# Estruturas, métodos e interfaces

[**Você pode encontrar todos os códigos desse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/estruturas-metodos-e-interfaces)

Suponha que precisamos de algum código de geometria para calcular o perímetro de um retângulo dado uma altura e largura. Podemos escrever uma função `Perimetro(largura float64, altura float64)`, onde `float64` representa números em ponto flutuante como `123.45`.

O ciclo de TDD deve ser mais familiar para você agora.

## Escreva o teste primeiro

```go
func TestPerimetro(t *testing.T) {
    resultado := Perimetro(10.0, 10.0)
    esperado := 40.0

    if resultado != esperado {
        t.Errorf("resultado %.2f esperado %.2f", resultado, esperado)
    }
}
```

Viu a nova string de formatação? O `f` é para nosso `float64` e o `.2` significa imprimir duas casas decimais.

## Execute o teste

`./formas_test.go:6:9: undefined: Perimetro`

`indefinido: Perimetro`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhando

```go
func Perimetro(largura float64, altura float64) float64 {
    return 0
}
```

Resulta em `formas_test.go:10: resultado 0, esperado 40`.

## Escreva código o suficiente para fazer o teste passar

```go
func Perimetro(largura float64, altura float64) float64 {
    return 2 * (largura + altura)
}
```

Por enquanto, tudo fácil. Agora vamos criar uma função chamada `Area(largura, altura float64)` que retorna a área de um retângulo.

Tente fazer isso sozinho, segundo o ciclo de TDD.

Você deve terminar com os testes como estes:

```go
func TestPerimetro(t *testing.T) {
    resultado := Perimetro(10.0, 10.0)
    esperado := 40.0

    if resultado != esperado {
        t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
    }
}

func TestArea(t *testing.T) {
    resultado := Area(12.0, 6.0)
    esperado := 72.0

    if resultado != esperado {
        t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
    }
}
```

E código como este:

```go
func Perimetro(largura float64, altura float64) float64 {
    return 2 * (largura + altura)
}

func Area(largura float64, altura float64) float64 {
    return largura * altura
}
```

## Refatoração

Nosso código faz o trabalho, mas não contém nada explícito sobre retângulos. Uma pessoa descuidada poderia tentar passar a largura e altura de um triângulo para esta função sem perceber que ela retornará uma resposta errada.

Podemos apenas dar para a função um nome mais específico como `AreaDoRetangulo`. Uma solução mais limpa é definir nosso próprio *tipo* chamado `Retangulo` que encapsula este conceito para nós.

Podemos criar um tipo simples usando uma **struct** (estrutura). [Uma struct](https://golang.org/ref/spec#Struct_types) é apenas uma coleção nomeada de campos onde você pode armazenar dados.

Declare uma `struct` assim:

```go
type Retangulo struct {
    Largura float64
    Altura  float64
}
```

Agora vamos refatorar os testes para usar `Retangulo` em vez de um simples `float64`.

```go
func TestPerimetro(t *testing.T) {
    retangulo := Retangulo{10.0, 10.0}
    resultado := Perimetro(retangulo)
    esperado := 40.0

    if resultado != esperado {
        t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
    }
}

func TestArea(t *testing.T) {
    retangulo := Retangulo{12.0, 6.0}
    resultado := Area(retangulo)
    esperado := 72.0

    if resultado != esperado {
        t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
    }
}
```

Lembre de rodar seus testes antes de tentar corrigir. Você deve ter erro útil como:

```
./formas_test.go:7:18: not enough arguments in call to Perimetro
    have (Retangulo)
    esperado (float64, float64)
```

Você pode acessar os campos de uma `struct` com a sintaxe `minhaStruct.campo`.

Mude as duas funções para corrigir o teste.

```go
func Perimetro(retangulo Retangulo) float64 {
    return 2 * (retangulo.Largura + retangulo.Altura)
}

func Area(retangulo Retangulo) float64 {
    return retangulo.Largura * retangulo.Altura
}
```

Espero que você concorde que passar um `Retangulo` para a função mostra nossa intenção com mais clareza, mas existem mais benefícios em usar `structs` que já vamos entender.

Nosso próximo requisito é escrever uma função `Area` para círculos.

## Escreva o teste primeiro

```go
func TestArea(t *testing.T) {
    t.Run("retângulos", func(t *testing.T) {
        retangulo := Retangulo{12.0, 6.0}
        resultado := Area(retangulo)
        esperado := 72.0

        if resultado != esperado {
            t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
        }
    })

    t.Run("círculos", func(t *testing.T) {
        circulo := Circulo{10}
        resultado := Area(circulo)
        esperado := 314.1592653589793

        if resultado != esperado {
            t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
        }
    })
}
```

## Execute o teste

`./formas_test.go:28:13: undefined: Circulo`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhando

Precisamos definir nosso tipo `Circulo`.

```go
type Circulo struct {
    Raio float64
}
```

Agora rode os testes novamente.

`./formas_test.go:29:14: cannot use circulo (type Circulo) as type Retangulo in argument to Area`

Algumas linguagens de programação permitem você fazer algo como:

```go
func Area(circulo Circulo) float64 { ... }
func Area(retangulo Retangulo) float64 { ... }
```

Mas em Go você não pode:

`./formas.go:20:32: Area redeclared in this block`

Temos duas escolhas:

* Podemos ter funções com o mesmo nome declaradas em *pacotes* diferentes. Então, poderíamos criar nossa `Area(Circulo)` em um novo *pacote*, só que isso parece um exagero aqui.
* Em vez disso, podemos definir [*métodos*](https://golang.org/ref/spec#Method_declarations) em nosso mais novo tipo definido.

### O que são métodos?

Até agora só escrevemos *funções*, mas temos usado alguns métodos. Quando chamamos `t.Errorf`, nós chamamos o método `Errorf` na instância de nosso `t` (`testing.T`).

Um método é uma função com um receptor. Uma declaração de método vincula um identificador e o nome do método a um método e associa o método com o tipo base do receptor.

Métodos são muito parecidos com funções, mas são chamados invocando-os em uma instância de um tipo específico.

Enquanto você chama funções onde quiser, como por exemplo em `Area(retangulo)`, você só pode chamar métodos em "coisas" específicas.

Um exemplo ajudará. Então, vamos mudar nossos testes primeiro para chamar métodos em vez de funções, e, em seguida, corrigir o código.

```go
func TestArea(t *testing.T) {
    t.Run("retângulos", func(t *testing.T) {
        retangulo := Retangulo{12.0, 6.0}
        resultado := retangulo.Area()
        esperado := 72.0

        if resultado != esperado {
            t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
        }
    })

    t.Run("círculos", func(t *testing.T) {
        circulo := Circulo{10}
        resultado := circulo.Area()
        esperado := 314.1592653589793

        if resultado != esperado {
            t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
        }
    })
}
```

Se rodarmos os testes agora, recebemos:

```
./formas_test.go:19:19: retangulo.Area undefined (type Retangulo has no field or method Area)
./formas_test.go:29:16: circulo.Area undefined (type Circulo has no field or method Area)
```

> type Circulo has no field or method Area

Gostaria de reforçar o quão grandioso o compilador é. É muito importante ter tempo para ler lentamente as mensagens de erro que você recebe, pois isso te ajudará a longo prazo.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhando

Vamos adicionar alguns métodos para nossos tipos:

```go
type Retangulo struct {
    Largura  float64
    Altura float64
}

func (r Retangulo) Area() float64  {
    return 0
}

type Circulo struct {
    Raio float64
}

func (c Circulo) Area() float64  {
    return 0
}
```

A sintaxe para declaração de métodos é quase a mesma que usamos para funções e isso acontece porque eles são muito parecidos. A única diferença é a sintaxe para o método receptor: `func (nomeDoReceptor TipoDoReceptor) NomeDoMetodo(argumentos)`.

Quando seu método é chamado em uma variável desse tipo, você tem sua referência para o dado através da variável `nomeDoReceptor`. Em muitas outras linguagens de programação isto é feito implicitamente e você acessa o receptor através de `this`.

É uma convenção em Go que a variável receptora seja a primeira letra do tipo em minúsculo.

```go
r Retangulo
```

Se você executar novamente os testes, eles devem compilar e dar alguma saída do teste falhando.

## Escreva código suficiente para fazer o teste passar

Agora vamos fazer nossos testes de retângulo passarem corrigindo nosso novo método.

```go
func (r Retangulo) Area() float64  {
    return r.Largura * r.Altura
}
```

Se você executar novamente os testes, aqueles de retângulo devem passar, mas os de círculo ainda falham.

Para fazer a função `Area` de círculo passar, vamos emprestar a constante `Pi` do pacote `math` (lembre-se de importá-lo).

```go
func (c Circulo) Area() float64  {
    return math.Pi * c.Raio * c.Raio
}
```

## Refatoração

Existe duplicação em nossos testes.

Tudo o que queremos fazer é pegar uma coleção de *formas*, chamar o método `Area()` e então verificar o resultado.

Queremos ser capazes de escrever um tipo de função `verificaArea` que permita passar tanto `Retangulo` quanto `Circulo`, mas falhe ao compilar se tentarmos passar algo que não seja uma *forma*.

Com Go, podemos trabalhar dessa forma com **interfaces**.

[Interfaces](https://golang.org/ref/spec#Interface_types) são um conceito muito poderoso em linguagens de programação estaticamente tipadas, como Go, porque permitem que você crie funções que podem ser usadas com diferentes tipos e permite a criação de código altamente desacoplado, mantendo ainda a segurança de tipos.

Vamos apresentar isso refatorando nossos testes.

```go
func TestArea(t *testing.T) {
    verificaArea := func(t *testing.T, forma Forma, esperado float64) {
        t.Helper()
        resultado := forma.Area()

        if resultado != esperado {
            t.Errorf("resultado %.2f, esperado %.2f", resultado, esperado)
        }
    }

    t.Run("retângulos", func(t *testing.T) {
        retangulo := Retangulo{12.0, 6.0}
        verificaArea(t, retangulo, 72.0)
    })

    t.Run("círculos", func(t *testing.T) {
        circulo := Circulo{10}
        verificaArea(t, circulo, 314.1592653589793)
    })
}
```

Estamos criando uma função auxiliar como fizemos em outros exercícios, mas desta vez estamos pedindo que uma `Forma` seja passada. Se tentarmos chamá-la com algo que não seja uma *forma*, não vai compilar.

Como algo se torna uma *forma*? Precisamos apenas falar para o Go o que é uma `Forma` usando uma declaração de interface.

```go
type Forma interface {
    Area() float64
}
```

Estamos criando um novo `tipo`, assim como fizemos com `Retangulo` e `Circulo`, mas desta vez é uma `interface` em vez de uma `struct`.

Uma vez adicionado isso ao código, os testes passarão.

### Peraí, como assim?

A interface em Go bem diferente das interfaces na maioria das outras linguagens de programação. Normalmente você tem que escrever um código para dizer que `meu tipo Foo implementa a interface Bar`.

Só que no nosso caso:

* `Retangulo` tem um método chamado `Area` que retorna um `float64`, então satisfaz a interface `Forma`.
* `Circulo` tem um método chamado `Area` que retorna um `float64`, então satisfaz a interface `Forma`.
* `string` não tem esse método, então não satisfaz a interface.
* etc.

Em Go a **resolução de interface é implícita**. Se o tipo que você passar combinar com o que a interface está esperando, o código será compilado.

### Desacoplando

Veja como nossa função auxiliar não precisa se preocupar se a *forma* é um `Retangulo` ou um `Circulo` ou um `Triangulo`. Ao declarar uma interface, a função auxiliar está *desacoplada* de tipos concretos e tem apenas o método que precisa para fazer o trabalho.

Este tipo de abordagem - de usar interfaces para declarar **somente o que você precisa** - é muito importante no desenvolvimento de software e será coberto mais detalhadamente nas próximas seções.

## Refatoração adicional

Agora que você conhece as `structs`, podemos apresentar os "table driven tests" (testes orientados por tabela).

[Table driven tests](https://github.com/golang/go/wiki/TableDrivenTests) são úteis quando você quer construir uma lista de casos de testes que podem ser testados da mesma forma.

```go
func TestArea(t *testing.T) {
    testesArea := []struct {
        forma    Forma
        esperado float64
    }{
        {Retangulo{12, 6}, 72.0},
        {Circulo{10}, 314.1592653589793},
    }

    for _, tt := range testesArea {
        resultado := tt.forma.Area()
        if resultado != tt.esperado {
            t.Errorf("resultado %.2f, esperado %.2f", resultado, tt.esperado)
        }
    }
}
```

A única sintaxe nova aqui é a criação de uma "struct anônima", `testesArea`. Estamos declarando um slice de structs usando `[]struct` com dois campos, o `forma` e o `esperado`. Então preenchemos o slice com os casos.

Depois iteramos sobre eles assim como fazemos com qualquer outro slice, usando os campos da struct para executar nossos testes.

Dá para perceber como será muito fácil para uma pessoa inserir uma nova forma, implementar `Area` e então adicioná-la nos casos de teste. Além disso, se for encontrada uma falha em `Area`, é muito fácil adicionar um novo caso de teste para verificar antes de corrigi-la.

*Testes baseados em tabela* podem ser um item valioso em sua caixa de ferramentas, mas tenha certeza de que você precisa da sintaxe extra nos testes. Se você deseja testar várias implementações de uma interface ou se o dado passado para uma função tem muitos requisitos diferentes que precisam de testes, eles podem servir bem.

Vamos demonstrar tudo isso adicionando e testando outra forma; um triângulo.

## Escreva o teste primeiro

Adicionar um teste para nossa nova forma é muito fácil. Simplesmente adicione `{Triangulo{12, 6}, 36.0},` à nossa lista.

```go
func TestArea(t *testing.T) {
    testesArea := []struct {
        forma    Forma
        esperado float64
    }{
        {Retangulo{12, 6}, 72.0},
        {Circulo{10}, 314.1592653589793},
        {Triangulo{12, 6}, 36.0},
    }

    for _, tt := range testesArea {
        resultado := tt.forma.Area()
        if resultado != tt.esperado {
            t.Errorf("resultado %.2f, esperado %.2f", resultado, tt.esperado)
        }
    }
}
```

## Execute o teste

Lembre-se, continue tentando executar o teste e deixe o compilador guiá-lo em direção a solução.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste falhando

`./formas_test.go:25:4: undefined: Triangulo`

Ainda não definimos `Triangulo`:

```go
type Triangulo struct {
    Base   float64
    Altura float64
}
```

Tente novamente:

```
./formas_test.go:25:8: cannot use Triangulo literal (type Triangulo) as type Forma in field value:
    Triangulo does not implement Forma (missing Area method)
```

`Triangulo não implementa Forma (método Area faltando)`

Isso nos diz que não podemos usar um `Triangulo` como uma `Forma` porque ele não tem um método `Area()`, então adicione uma implementação vazia para fazermos o teste funcionar:

```go
func (t Triangulo) Area() float64 {
    return 0
}
```

Finalmente o código compilou e temos o nosso erro:

`formas_test.go:31: resultado 0.00, esperado 36.00`

## Escreva código suficiente para fazer o teste passar

```go
func (t Triangulo) Area() float64 {
    return (t.Base * t.Altura) * 0.5
}
```

E nossos testes passaram!

## Refatoração

Novamente, a implementação está boa, mas nossos testes podem ser melhorados.

Quando você lê isso:

```go
{Retangulo{12, 6}, 72.0},
{Circulo{10}, 314.1592653589793},
{Triangulo{12, 6}, 36.0},
```

Não está tão claro o que todos os números representam e você deve ter o objetivo de escrever testes que sejam fáceis de entender.

Até agora você viu uma sintaxe para criar instâncias de structs como `MinhaStruct{valor1, valor2}`, mas você pode opcionalmente nomear esses campos.

Vamos ver como isso funciona:

```go
        {forma: Retangulo{largura: 12, altura: 6}, esperado: 72.0},
        {forma: Circulo{Raio: 10}, esperado: 314.1592653589793},
        {forma: Triangulo{Base: 12, altura: 6}, esperado: 36.0},
```

Em [Test-Driven Development by Example](https://g.co/kgs/yCzDLF) Kent Beck refatora alguns testes para um ponto e afirma:

> O teste é lido de forma mais clara, como se fosse uma afirmação da verdade, **não uma sequência de operações**

(ênfase minha)

Agora nossos testes (pelo menos a lista de casos) fazem afirmações da verdade sobre formas e suas áreas.

## Garanta que a saída do seu teste seja útil

Lembra anteriormente quando implementamos `Triangulo` e tivemos um teste falhando? Ele imprimiu `formas_test.go:31: resultado 0.00 esperado, 36.00`.

Nós sabíamos que estava relacionado ao `Triangulo` porque estávamos trabalhando nisso, mas e se uma falha escorregasse para o sistema em um dos 20 casos na tabela? Como alguém saberia qual caso falhou? Não parece ser uma boa experiência. Ela teria que olhar caso a caso para encontrar qual deles está falhando de fato.

Podemos mudar nossa mensagem de erro para `%#v resultado %.2f, esperado %.2f`. A string de formatação `%#v` irá imprimir nossa struct com os valores em seu campo para que as pessoas possam ver imediatamente as propriedades que estão sendo testadas.

Para melhorar a legibilidade de nossos futuros casos de teste, podemos renomear o campo `esperado` para algo mais descritivo como `temArea`.

Uma dica final com testes guiados por tabela é usar `t.Run` e renomear os casos de teste.

Envolvendo cada caso em um `t.Run` você terá uma saída de testes mais limpa em caso de falhas, além de imprimir o nome do caso.

```
--- FAIL: TestArea (0.00s)
    --- FAIL: TestArea/Retangulo (0.00s)
        formas_test.go:33: main.Retangulo{Largura:12, Altura:6} resultado 72.00, esperado 72.10
```

E você pode rodar testes específicos dentro de sua tabela com `go test -run TestArea/Retangulo`.

Aqui está o código final do nosso teste que captura isso:

```go
func TestArea(t *testing.T) {
    testesArea := []struct {
        nome    string
        forma   Forma
        temArea float64
    }{
        {nome: "Retângulo", forma: Retangulo{Largura: 12, Altura: 6}, temArea: 72.0},
        {nome: "Círculo", forma: Circulo{Raio: 10}, temArea: 314.1592653589793},
        {nome: "Triângulo", forma: Triangulo{Base: 12, Altura: 6}, temArea: 36.0},
    }

    for _, tt := range testesArea {
        t.Run(tt.nome, func(t *testing.T) {
            resultado := tt.forma.Area()
            if resultado != tt.temArea {
                t.Errorf("%#v resultado %.2f, esperado %.2f", tt.forma, resultado, tt.temArea)
            }
        })
    }
}
```

## Resumo

Esta foi mais uma prática de TDD, iterando em nossas soluções para problemas matemáticos básicos e aprendendo novos recursos da linguagem motivados por nossos testes.

* Declarar structs para criar seus próprios tipos de dados permite agrupar dados relacionados e torna a intenção do seu código mais clara.
* Declarar interfaces permite que você possa definir funções que podem ser usadas por diferentes tipos ([polimorfismo paramétrico](https://pt.wikipedia.org/wiki/Polimorfismo_paramétrico)).
* Adicionar métodos permite que você possa adicionar funcionalidades aos seus tipos de dados e implementar interfaces.
* Testes baseados em tabela permite que você torne suas asserções mais claras e seus testes mais fáceis de estender e manter.

Este foi um capítulo importante porque agora começamos a definir nossos próprios tipos. Em linguagens estaticamente tipadas como Go, conseguir projetar seus próprios tipos é essencial para construir software que seja fácil de entender, compilar e testar.

Interfaces são uma ótima ferramenta para ocultar a complexidade de outras partes do sistema. Em nosso caso, o *código* de teste auxiliar não precisou conhecer a forma exata que estava afirmando, apenas como "pedir" pela sua área.

Conforme você se familiariza com Go, começa a ver a força real das interfaces e da biblioteca padrão.

Você aprenderá sobre as interfaces definidas na biblioteca padrão que são usadas *em todo lugar* e, implementando-as em relação aos seus próprios tipos, você pode reutilizar rapidamente muitas das ótimas funcionalidades.


# Ponteiros e erros

[**Você pode encontrar todos os códigos deste capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/ponteiros-e-erros)

Aprendemos sobre estruturas na última seção, o que nos possibilita capturar valores com conceito relacionado.

Em algum momento talvez você deseje utilizar estruturas para gerenciar valores, expondo métodos que permita aos usuários mudá-los de um jeito que você possa controlar.

[**Fintechs**](https://www.infowester.com/fintech.php) **amam Go** e uhh bitcoins? Então vamos mostrar um sistema bancário incrível que podemos construir.

Vamos construir uma estrutura de `Carteira` que possamos depositar `Bitcoin`.

## Escreva o teste primeiro

```go
func TestCarteira(t *testing.T) {
    carteira := Carteira{}

    carteira.Depositar(10)

    resultado := carteira.Saldo()
    esperado := 10

    if resultado != esperado {
        t.Errorf("resultado %d, esperado %d", resultado, esperado)
    }
}
```

No [exemplo anterior](/aprenda-go-com-testes/primeiros-passos-com-go/estruturas-metodos-e-interfaces) acessamos campos diretamente pelo nome. Entretanto, na nossa *carteira super protegida*, não queremos expor o valor interno para o resto do mundo. Queremos controlar o acesso por meio de métodos.

## Execute o teste

`./carteira_test.go:7:12: undefined: Carteira`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

O compilador não sabe o que uma `Carteira` é, então vamos declará-la.

```go
type Carteira struct { }
```

Agora que declaramos nossa carteira, tente rodar o teste novamente:

```go
./carteira_test.go:9:8: carteira.Depositar undefined (type Carteira has no field or method Depositar)
./carteira_test.go:11:15: carteira.Saldo undefined (type Carteira has no field or method Saldo)
```

Precisamos definir estes métodos.

Lembre-se de apenas fazer o necessário para fazer os testes rodarem. Precisamos ter certeza que nossos testes falhem corretamente com uma mensagem de erro clara.

```go
func (c Carteira) Depositar(quantidade int) {

}

func (c Carteira) Saldo() int {
    return 0
}
```

Se essa sintaxe não for familiar, dê uma lida na seção de estruturas.

Os testes agora devem compilar e rodar:

`carteira_test.go:15: resultado 0, esperado 10`

## Escreva código o suficiente para fazer o teste passar

Precisaremos de algum tipo de variável de *saldo* em nossa estrutura para guardar o valor:

```go
type Carteira struct {
    saldo int
}
```

Em Go, se uma variável, tipo, função e etc, começam com uma letra minúsculo, então esta será privada para *outros pacotes que não seja o que a definiu*.

No nosso caso, queremos que apenas nossos métodos sejam capazes de manipular os valores.

Lembre-se que podemos acessar o valor interno do campo `saldo` usando a variável "receptora".

```go
func (c Carteira) Depositar(quantidade int) {
    c.saldo += quantidade
}

func (c Carteira) Saldo() int {
    return c.saldo
}
```

Com a nossa carreira em Fintechs segura, rode os testes para nos aquecermos para passarmos no teste.

`carteira_test.go:15: resultado 0, esperado 10`

### ????

Ok, isso é confuso. Parece que nosso código deveria funcionar, pois adicionamos nosso novo valor ao saldo e o método Saldo deveria retornar o valor atual.

Em Go, **quando uma função ou um método é invocado, os argumentos são** ***copiados***.

Quando `func (c Carteira) Depositar(quantidade int)` é chamado, o `c` é uma cópia do valor de qualquer lugar que o método tenha sido chamado.

Sem focar em Ciência da Computação, quando criamos um valor (como uma carteira), esse valor é alocado em algum lugar da memória. Você pode descobrir o *endereço* desse bit de memória usando `&meuValor`.

Experimente isso adicionando alguns prints no código:

```go
func TestCarteira(t *testing.T) {
    carteira := Carteira{}

    carteira.Depositar(10)

    resultado := carteira.Saldo()

    fmt.Printf("O endereço do saldo no teste é %v \n", &carteira.saldo)

    esperado := 10

    if resultado != esperado {
        t.Errorf("resultado %d, esperado %d", resultado, esperado)
    }
}
```

```go
func (c Carteira) Depositar(quantidade int) {
    fmt.Printf("O endereço do saldo no Depositar é %v \n", &c.saldo)
    c.saldo += quantidade
}
```

O `\n` é um caractere de escape queeadiciona uma nova linha após imprimir o endereço de memória. Conseguimos acessar o ponteiro para algo com o símbolo de endereço `&`.

Agora rode o teste novamente:

```
O endereço do saldo no Depositar é 0xc420012268
O endereço do saldo no teste é is 0xc420012260
```

Podemos ver que os endereços dos dois saldos são diferentes. Então, quando mudamos o valor de um dos saldos dentro do código, estamos trabalhando em uma cópia do que veio do teste. Portanto, o saldo no teste não é alterado.

Podemos consertar isso com *ponteiros*. [Ponteiros](https://gobyexample.com/pointers) nos permitem *apontar* para alguns valores e então mudá-los. Então, em vez de termos uma cópia da Carteira, usamos um ponteiro para a carteira para que possamos alterá-la.

```go
func (c *Carteira) Depositar(quantidade int) {
    c.saldo += quantidade
}

func (c *Carteira) Saldo() int {
    return c.saldo
}
```

A diferença é que o tipo do argumento é `*Carteira` em vez de `Carteira` que você pode ler como "um ponteiro para uma carteira".

Rode novamente os testes e eles devem passar.

## Refatoração

Dissemos que estávamos fazendo uma carteira Bitcoin, mas até agora não os mencionamos. Estamos usando `int` porque é um bom tipo para contar coisas!

Parece um pouco exagerado criar uma `struct` para isso. `int` é o suficiente nesse contexto, mas não é descritivo o suficiente.

Go permite criarmos novos tipos a partir de tipos existentes.

A sintaxe é `type MeuNome TipoOriginal`

```go
type Bitcoin int

type Carteira struct {
    saldo Bitcoin
}

func (c *Carteira) Depositar(quantidade Bitcoin) {
    c.saldo += quantidade
}

func (c *Carteira) Saldo() Bitcoin {
    return c.saldo
}
```

```go
func TestCarteira(t *testing.T) {

    carteira := Carteira{}

    carteira.Depositar(Bitcoin(10))

    resultado := carteira.Saldo()

    esperado := Bitcoin(10)

    if resultado != esperado {
            t.Errorf("resultado %d, esperado %d", resultado, esperado)
        }
}
```

Para criarmos `Bitcoin`, basta usar a sintaxe `Bitcoin(999)`.

Ao fazermos isso, estamos criando um novo tipo e podemos declarar *métodos* nele. Isto pode ser muito útil quando queremos adicionar funcionalidades de domínios específicos a tipos já existentes.

Vamos implementar um [Stringer](https://golang.org/pkg/fmt/#Stringer) para o Bitcoin:

```go
type Stringer interface {
        String() string
}
```

Essa interface é definida no pacote `fmt` e permite definir como seu tipo é impresso quando utilizado com o operador de string `%s` em prints.

```go
func (b Bitcoin) String() string {
    return fmt.Sprintf("%d BTC", b)
}
```

Como podemos ver, a sintaxe para criar um método em um tipo definido por nós é a mesma que a utilizada em uma struct.

Agora precisamos atualizar nossas impressões de strings no teste para que usem `String()`.

```go
    if resultado != esperado {
        t.Errorf("resultado %s, esperado %s", resultado, esperado)
    }
```

Para ver funcionando, quebre o teste de propósito para que possamos ver:

`carteira_test.go:18: resultado 10 BTC, esperado 20 BTC`

Isto deixa mais claro o que está acontecendo em nossos testes.

O próximo requisito é criar uma função de `Retirar`.

## Escreva o teste primeiro

É basicamente o aposto da função `Depositar()`:

```go
func TestCarteira(t *testing.T) {
    t.Run("Depositar", func(t *testing.T) {
        carteira := Carteira{}

        carteira.Depositar(Bitcoin(10))

        resultado := carteira.Saldo()

        esperado := Bitcoin(10)

        if resultado != esperado {
            t.Errorf("resultado %s, esperado %s", resultado, esperado)
        }
    })

    t.Run("Retirar", func(t *testing.T) {
        carteira := Carteira{saldo: Bitcoin(20)}

        carteira.Retirar(Bitcoin(10))

        resultado := carteira.Saldo()

        esperado := Bitcoin(10)

        if resultado != esperado {
            t.Errorf("resultado %s, esperado %s", resultado, esperado)
        }
    })
}
```

## Execute o teste

`./carteira_test.go:26:9: carteira.Retirar undefined (type Carteira has no field or method Retirar)`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (c *Carteira) Retirar(quantidade Bitcoin) {
}
```

`carteira_test.go:33: resultado 20 BTC, esperado 10 BTC`

## Escreva código o suficiente para fazer o teste passar

```go
func (c *Carteira) Retirar(quantidade Bitcoin) {
    c.saldo -= quantidade
}
```

## Refatoração

Há algumas duplicações em nossos testes, vamos refatorar isso.

```go
func TestCarteira(t *testing.T) {
    confirmaSaldo := func(t *testing.T, carteira Carteira, esperado Bitcoin) {
        t.Helper()
        resultado := carteira.Saldo()

        if resultado != esperado {
            t.Errorf("resultado %s, esperado %s", resultado, esperado)
        }
    }

    t.Run("Depositar", func(t *testing.T) {
        carteira := Carteira{}
        carteira.Depositar(Bitcoin(10))
        confirmaSaldo(t, carteira, Bitcoin(10))
    })

    t.Run("Retirar", func(t *testing.T) {
        carteira := Carteira{saldo: Bitcoin(20)}
        carteira.Retirar(10)
        confirmaSaldo(t, carteira, Bitcoin(10))
    })
}
```

O que aconteceria se você tentasse `Retirar` mais do que há de saldo na conta? Por enquanto, nossos requisitos são assumir que não há nenhum tipo de cheque-especial.

Como sinalizamos um problema quando estivermos usando `Retirar` ?

Em Go, se você quiser indicar um erro, sua função deve retornar um `err` para que quem a chamou possar verificá-lo e tratá-lo.

Vamos tentar fazer isso em um teste.

## Escreva o teste primeiro

```go
t.Run("Retirar com saldo insuficiente", func(t *testing.T) {
    saldoInicial := Bitcoin(20)
    carteira := Carteira{saldoInicial}
    erro := carteira.Retirar(Bitcoin(100))

    confirmaSaldo(t, carteira, saldoInicial)

    if erro == nil {
        t.Error("Esperava um erro mas nenhum ocorreu")
    }
})
```

Queremos que `Retirar` retorne um erro se tentarmos retirar mais do que temos e o saldo deverá continuar o mesmo.

Verificamos se um erro foi retornado falhando o teste se o valor for `nil`.

`nil` é a mesma coisa que `null` de outras linguagens de programação.

Erros podem ser `nil`, porque o tipo do retorno de `Retirar` vai ser `error`, que é uma interface. Se você vir uma função que tem argumentos ou retornos que são interfaces, eles podem ser nulos.

Do mesmo jeito que `null`, se tentarmos acessar um valor que é `nil`, isso irá disparar um **pânico em tempo de execução**. Isso é ruim! Devemos ter certeza que tratamos os valores nulos.

## Execute o teste

`./carteira_test.go:31:25: carteira.Retirar(Bitcoin(100)) used as value`

Talvez não esteja tão claro, mas nossa intenção era apenas invocar a função `Retirar` e ela nunca irá retornar um valor pois o saldo será diretamente subtraído com o ponteiro e a função deve apenas retornar o erro (se houver). Para fazer compilar, precisaremos mudar a função para que retorne um tipo.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (c *Carteira) Retirar(quantidade Bitcoin) error {
    c.saldo -= quantidade
    return nil
}
```

Novamente, é muito importante escrever apenas o suficiente para compilar. Corrigimos o método `Retirar` para retornar `error` e por enquanto temos que retornar *alguma coisa*, então vamos apenas retornar `nil` .

## Escreva código o suficiente para fazer o teste passar

```go
func (c *Carteira) Retirar(quantidade Bitcoin) error {
    if quantidade > c.saldo {
        return errors.New("eita")
    }

    c.saldo -= quantidade
    return nil
}
```

Lembre-se de importar `errors`.

`errors.New` cria um novo `error` com a mensagem escolhida.

## Refatoração

Vamos fazer um método auxiliar de teste para nossa verificação de erro para deixar nosso teste mais legível.

```go
confirmaErro := func(t *testing.T, erro error) {
    t.Helper()
    if erro == nil {
        t.Error("esperava um erro, mas nenhum ocorreu.")
    }
}
```

E em nosso teste:

```go
t.Run("Retirar com saldo insuficiente", func(t *testing.T) {
    saldoInicial := Bitcoin(20)
    carteira := Carteira{saldoInicial}
    erro := carteira.Retirar(Bitcoin(100))

    confirmaSaldo(t, carteira, saldoInicial)
    confirmaErro(t, erro)
})
```

Espero que, ao retornamos um erro do tipo "eita", você pense que *devêssemos* deixar mais claro o que ocorreu, já que esta não parece uma informação útil para nós.

Assumindo que o erro enfim foi retornado para o usuário, vamos atualizar nosso teste para verificar o tipo espcífico de mensagem de erro ao invés de apenas verificar se um erro existe.

## Escreva o teste primeiro

Atualize nosso helper para comparar com uma `string`:

```go
confirmaErro := func(t *testing.T, valor error, esperado string) {
    t.Helper()
    if valor == nil {
        t.Fatal("esperava um erro, mas nenhum ocorreu")
    }

     resultado := valor.Error()

    if resultado != esperado {
        t.Errorf("resultado %s, esperado %s", resultado, esperado)
    }
}
```

E então atualize o invocador:

```go
t.Run("Retirar saldo insuficiente", func(t *testing.T) {
    saldoInicial := Bitcoin(20)
    carteira := Carteira{saldoInicial}
    erro := carteira.Retirar(Bitcoin(100))

    confirmaSaldo(t, carteira, saldoInicial)
    confirmaErro(t, erro, "não é possível retirar: saldo insuficiente")
})
```

Usamos o `t.Fatal` que interromperá o teste se for chamado. Isso é feito porque não queremos fazer mais asserções no erro retornado, se não houver um. Sem isso, o teste continuaria e causaria erros por causa do ponteiro `nil`.

## Execute o teste

`carteira_test.go:61: erro resultado 'eita', erro esperado 'não é possível retirar: saldo insuficiente'`

## Escreva código o suficiente para fazer o teste passar

```go
func (c *Carteira) Retirar(quantidade Bitcoin) error {

    if quantidade > c.saldo {
        return errors.New("não é possível retirar: saldo insuficiente")
    }

    c.saldo -= quantidade
    return nil
}
```

## Refatoração

Temos duplicação da mensagem de erro tanto no código de teste quanto no código de `Retirar`.

Seria chato se o teste falhasse por alguém ter mudado a mensagem do erro e é muito detalhe para o nosso teste. Nós não *necessariamente* nos importamos qual mensagem é exatamente, apenas que algum tipo de erro significativo sobre a função é retornado dada uma certa condição.

Em Go, erros são valores, então podemos refatorar isso para ser uma variável e termos apenas uma fonte da verdade.

```go
var ErroSaldoInsuficiente = errors.New("não é possível retirar: saldo insuficiente")

func (c *Carteira) Retirar(quantidade Bitcoin) error {

    if quantidade > c.saldo {
        return ErroSaldoInsuficiente
    }

    c.saldo -= quantidade
    return nil
}
```

A palavra-chave `var` no escopo do arquivo nos permite definir valores globais para o pacote.

Esta é uma mudança positiva, pois agora nossa função `Retirar` parece mais limpa.

Agora, podemos refatorar nosso código para usar este valor ao invés de uma string específica.

```go
func TestCarteira(t *testing.T) {
    t.Run("Depositar", func(t *testing.T) {
        carteira := Carteira{}
        carteira.Depositar(Bitcoin(10))

        confirmaSaldo(t, carteira, Bitcoin(10))
    })

    t.Run("Retirar com saldo suficiente", func(t *testing.T) {
        carteira := Carteira{Bitcoin(20)}
        erro := carteira.Retirar(Bitcoin(10))

        confirmaSaldo(t, carteira, Bitcoin(10))
    })

    t.Run("Retirar com saldo insuficiente", func(t *testing.T) {
        saldoInicial := Bitcoin(20)
        carteira := Carteira{saldoInicial}
        erro := carteira.Retirar(Bitcoin(100))

        confirmaSaldo(t, carteira, saldoInicial)
        confirmaErro(t, erro, ErroSaldoInsuficiente)
    })
}

func confirmaSaldo(t *testing.T, carteira Carteira, esperado Bitcoin) {
    t.Helper()
    resultado := carteira.Saldo()

    if resultado != esperado {
        t.Errorf("resultado %s, esperado %s", resultado, esperado)
    }
}

func confirmaErro(t *testing.T, resultado error, esperado error) {
    t.Helper()
    if resultado == nil {
        t.Fatal("esperava um erro, mas nenhum ocorreu")
    }

    if resultado != esperado {
        t.Errorf("erro resultado %s, erro esperado %s", resultado, esperado)
    }
}
```

Agora está mais fácil dar continuidade ao nosso teste.

Nós apenas movemos os métodos auxiliares para fora da função principal de teste. Logo, quando alguém abrir o arquivo, começará lendo nossas asserções primeiro ao invés desses métodos auxiliares.

Outra propriedade útil de testes é que eles nos ajudam a entender o uso *real* do nosso código e assim podemos fazer códigos mais compreensivos. Podemos ver aqui que um desenvolvedor pode simplesmente chamar nosso código e fazer uma comparação de igualdade a `ErroSaldoInsuficiente`, e então agir de acordo.

### Erros não verificados

Embora o compilador do Go ajude bastante, há coisas que você pode acabar errando e o tratamento de erro pode se tornar complicado.

Há um cenário que nós não testamos. Para descobri-lo, execute o comando a seguir no terminal para instalar o `errcheck`, um dos muitos linters disponíveis em Go.

`go get -u github.com/kisielk/errcheck`

Então, dentro do diretório do seu código, execute `errcheck .`.

Você deve receber algo assim:

`carteira_test.go:17:18: carteira.Retirar(Bitcoin(10))`

O que isso está nos dizendo é que não verificamos o erro sendo retornado naquela linha de código. Aquela linha de código, no meu computador, corresponde para o nosso cenário normal de retirada, porque não verificamos que se `Retirar` é bem sucedido quando um erro *não* é retornado.

Aqui está o código de teste final que resolve isto.

```go
func TestCarteira(t *testing.T) {
    t.Run("Depositar", func(t *testing.T) {
        carteira := Carteira{}
        carteira.Depositar(Bitcoin(10))

        confirmaSaldo(t, carteira, Bitcoin(10))
    })

    t.Run("Retirar com saldo suficiente", func(t *testing.T) {
        carteira := Carteira{Bitcoin(20)}
        erro := carteira.Retirar(Bitcoin(10))

        confirmaSaldo(t, carteira, Bitcoin(10))
        confirmaErroInexistente(t, erro)
    })

    t.Run("Retirar com saldo insuficiente", func(t *testing.T) {
        saldoInicial := Bitcoin(20)
        carteira := Carteira{saldoInicial}
        erro := carteira.Retirar(Bitcoin(100))

        confirmaSaldo(t, carteira, saldoInicial)
        confirmaErro(t, erro, ErroSaldoInsuficiente)
    })
}

func confirmaSaldo(t *testing.T, carteira Carteira, esperado Bitcoin) {
    t.Helper()
    resultado := carteira.Saldo()

    if resultado != esperado {
        t.Errorf("resultado %s, esperado %s", resultado, esperado)
    }
}

func confirmaErroInexistente(t *testing.T, resultado error) {
    t.Helper()
    if resultado != nil {
        t.Fatal("erro inesperado recebido")
    }
}

func confirmaErro(t *testing.T, resultado error, esperado error) {
    t.Helper()
    if resultado == nil {
        t.Fatal("esperava um erro, mas nenhum ocorreu")
    }

    if resultado != esperado {
        t.Errorf("erro resultado %s, erro esperado %s", resultado, esperado)
    }
}
```

## Resumo

### Ponteiros

* Go copia os valores quando são passados para funções/métodos. Então, se estiver escrevendo uma função que precise mudar o estado, você precisará de um ponteiro para o valor que você quer mudar.
* O fato de que Go pega um cópia dos valores é muito útil na maior parte do tempo, mas às vezes você não vai querer que o seu sistema faça cópia de alguma coisa. Nesse caso, você precisa passar uma referência. Podemos, por exemplo, ter dados muito grandes,  ou coisas que você talvez pretenda ter apenas uma instância (como conexões a banco de dados).

### nil

* Ponteiros podem ser `nil`.
* Quando uma função retorna um ponteiro para algo, você precisa ter certeza de verificar se ele é `nil` ou isso vai gerar uma exceção em tempo de execução, já que o compilador não te consegue te ajudar nesses casos.
* Útil para quando você quer descrever um valor que pode estar faltando.

### Erros

* Erros são a forma de sinalizar falhas na execução de uma função/método.
* Analisando nossos testes, concluímos que buscar por uma string em um erro poderia resultar em um teste não muito confiável. Então, refatoramos para usar um valor significativo, que resultou em um código mais fácil de ser testado e concluímos que também seria mais fácil para usuários de nossa API.
* Este não é o fim do assunto de tratamento de erros. Você pode fazer coisas mais sofisticadas, mas esta é apenas uma introdução. Capítulos posteriores vão abordar mais estratégias.
* [Não somente verifique os erros, trate-os graciosamente](https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully)

### Crie novos tipos a partir de existentes

* Útil para adicionar domínios mais específicos a valores
* Permite implementar interfaces

Ponteiros e erros são uma grande parte de escrita em Go que você precisa estar confortável. Por sorte, *na maioria das vezes* o compilador irá ajudar se você fizer algo errado. É só tomar um tempinho lendo a mensagem de erro.


# Maps

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/maps)

Em [arrays e slices](/aprenda-go-com-testes/primeiros-passos-com-go/arrays-e-slices), vimos como armazenar valores em ordem. Agora, vamos descobrir uma forma de armazenar itens por uma `key` (chave) e procurar por ela rapidamente.

Maps te permitem armazenar itens de forma parecida com a de um dicionário. Você pode pensar na `chave` como a palavra e o `valor` como a definição. E tem forma melhor de aprender sobre maps do que criar seu próprio dicionário?

Primeiro, vamos presumir que já temos algumas palavras com suas definições no dicionário. Se procurarmos por uma palavra, o dicionário deve retornar sua definição.

## Escreva o teste primeiro

Em `dicionario_test.go`

```go
package main

import "testing"

func TestBusca(t *testing.T) {
    dicionario := map[string]string{"teste": "isso é apenas um teste"}

    resultado := Busca(dicionario, "teste")
    esperado := "isso é apenas um teste"

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s', dado '%s'", resultado, esperado, "test")
    }
}
```

Declarar um map é bem parecido com declarar um array. A diferença é que começa com a palavra-chave `map` e requer dois tipos. O primeiro é o tipo da chave, que é escrito dentro de `[]`. O segundo é o tipo do valor, que vai logo após o `[]`.

O tipo da chave é especial. Só pode ser um tipo comparável, porque sem a habilidade de dizer se duas chaves são iguais, não temos como ter certeza de que estamos obtendo o valor correto. Tipos comparáveis são explicados com detalhes na [especificação da linguagem](https://golang.org/ref/spec#Comparison_operators) (em inglês).

O tipo do valor, por outro lado, pode ser o tipo que quiser. Pode até ser outro map.

O restante do teste já deve ser familiar para você.

## Execute o teste

Ao executar `go test`, o compilador vai falhar com `./dicionario_test.go:8:9: undefined: Busca`.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Em `dicionario.go`:

```go
package main

func Busca(dicionario map[string]string, palavra string) string {
    return ""
}
```

Agora seu teste vai falhar com uma *mensagem de erro clara*:

`dicionario_test.go:12: resultado '', esperado 'isso é apenas um teste', dado 'teste'`.

## Escreva código o suficiente para fazer o teste passar

```go
func Busca(dicionario map[string]string, palavra string) string {
    return dicionario[palavra]
}
```

Obter um valor de um map é igual a obter um valor de um array: `map[chave]`.

## Refatoração

```go
func TestBusca(t *testing.T) {
    dicionario := map[string]string{"teste": "isso é apenas um teste"}

    resultado := Busca(dicionario, "teste")
    esperado := "isso é apenas um teste"

    comparaStrings(t, resultado, esperado)
}

func comparaStrings(t *testing.T, resultado, esperado string) {
    t.Helper()

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s', dado '%s'", resultado, esperado, "teste")
    }
}
```

Decidi criar um helper `comparaStrings` para tornar a implementação mais genérica.

### Usando um tipo personalizado

Podemos melhorar o uso do nosso dicionário criando um novo tipo baseado no map e transformando a `Busca` em um método.

Em `dicionario_test.go`:

```go
func TestBusca(t *testing.T) {
    dicionario := Dicionario{"teste": "isso é apenas um teste"}

    resultado := dicionario.Busca("teste")
    esperado := "isso é apenas um teste"

    comparaStrings(t, resultado, esperado)
}
```

Começamos a usar o tipo `Dicionario`, que ainda não definimos. Depois disso, chamamos `Busca` da instância de `Dicionario`.

Não precisamos mudar o `comparaStrings`.

Em `dicionario.go`:

```go
type Dicionario map[string]string

func (d Dicionario) Busca(palavra string) string {
    return d[palavra]
}
```

Aqui criamos um tipo `Dicionario` que trabalha em cima da abstração de `map`. Com o tipo personalizado definido, podemos criar o método `Busca`.

## Escreva o teste primeiro

A busca básica foi bem fácil de implementar, mas o que acontece se passarmos uma palavra que não está no nosso dicionário?

Com o código atual, não recebemos nada de volta. Isso é bom porque o programa continua a ser executado, mas há uma abordagem melhor. A função pode reportar que a palavra não está no dicionário. Dessa forma, o usuário não fica se perguntando se a palavra não existe ou se apenas não existe definição para ela (isso pode não parecer tão útil para um dicionário. No entanto, é um caso que pode ser essencial em outros casos de uso).

```go
func TestBusca(t *testing.T) {
    dicionario := Dicionario{"teste": "isso é apenas um teste"}

    t.Run("palavra conhecida", func(t *testing.T) {
        resultado, _ := dicionario.Busca("teste")
        esperado := "isso é apenas um teste"

        comparaStrings(t, resultado, esperado)
    })

    t.Run("palavra desconhecida", func(t *testing.T) {
        _, err := dicionario.Busca("desconhecida")

        if err == nil {
            t.Fatal("é esperado que um erro seja obtido.")
        }
    })
}
```

A forma de lidar com esse caso no Go é retornar um segundo argumento que é do tipo `Error`.

Erros podem ser convertidos para uma string com o método `.Error()`, o que podemos fazer quando passarmos para a asserção. Também estamos protegendo o `comparaStrings` com `if` para certificar que não chamemos `.Error()` quando o erro for `nil`.

## Execute o teste

Isso não vai compilar.

`./dicionario_test.go:18:10: assignment mismatch: 2 variables but 1 values`

`incompatibilidade de atribuição: 2 variáveis, mas 1 valor`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (d Dicionario) Busca(palavra string) (string, error) {
    return d[palavra], nil
}
```

Agora seu teste deve falhar com uma mensagem de erro muito mais clara.

`dicionario_test.go:22: expected to get an error.`

`erro esperado.`

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Busca(palavra string) (string, error) {
    definicao, existe := d[palavra]
    if !existe {
        return "", errors.New("não foi possível encontrar a palavra que você procura")
    }

    return definicao, nil
}
```

Para fazê-lo passar, estamos usando uma propriedade interessante ao percorrer o map. Ele pode retornar dois valores. O segundo valor é uma boleana que indica se a chave foi encontrada com sucesso.

Essa propriedade nos permite diferenciar entre uma palavra que não existe e uma palavra que simplesmente não tem uma definição.

## Refatoração

```go
var ErrNaoEncontrado = errors.New("não foi possível encontrar a palavra que você procura")

func (d Dicionario) Busca(palavra string) (string, error) {
    definicao, existe := d[palavra]
    if !existe {
        return "", ErrNaoEncontrado
    }

    return definicao, nil
}
```

Podemos nos livrar do "erro mágico" na nossa função de `Busca` extraindo-o para dentro de uma variável. Isso também nos permite ter um teste melhor.

```go
t.Run("palavra desconhecida", func(t *testing.T) {
    _, resultado := dicionario.Busca("desconhecida")

    comparaErro(t, resultado, ErrNaoEncontrado)
})

func comparaErro(t *testing.T, resultado, esperado error) {
    t.Helper()

    if resultado != esperado {
        t.Errorf("resultado erro '%s', esperado '%s'", resultado, esperado)
    }
}
```

Conseguimos simplificar nosso teste criando um novo helper e começando a usar nossa variável `ErrNaoEncontrado` para que nosso teste não falhe se mudarmos o texto do erro no futuro.

## Escreva o teste primeiro

Temos uma ótima maneira de buscar no dicionário. No entanto, não temos como adicionar novas palavras nele.

```go
func TestAdiciona(t *testing.T) {
    dicionario := Dicionario{}
    dicionario.Adiciona("teste", "isso é apenas um teste")

    esperado := "isso é apenas um teste"
    resultado, err := dicionario.Busca("teste")
    if err != nil {
        t.Fatal("não foi possível encontrar a palavra adicionada:", err)
    }

    if esperado != resultado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

Nesse teste, estamos utilizando nossa função `Busca` para tornar a validação do dicionário um pouco mais fácil.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Em `dicionario.go`

```go
func (d Dicionario) Adiciona(palavra, definicao string) {
}
```

Agora seu teste deve falhar.

```bash
dicionario_test.go:31: deveria ter encontrado palavra adicionada: não foi possível encontrar a palavra que você procura
```

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Adiciona(palavra, definicao string) {
    d[palavra] = definicao
}
```

Adicionar coisas a um map também é bem semelhante a um array. Você só precisar especificar uma chave e definir qual é seu valor.

### Tipos Referência

Uma propriedade interessante dos maps é que você pode modificá-los sem passá-los como ponteiro. Isso é porque o `map` é um tipo referência. Isso significa que ele contém uma referência à estrutura de dado que estamos utilizando, assim como um ponteiro. Logo, quando criamos passamos o map como parâmetro, estamos alterando o map original e não sua cópia. A estrutura de dados utilizada é uma `tabela de dispersão` ou `mapa de hash`, e você pode ler mais sobre [aqui](https://pt.wikipedia.org/wiki/Tabela_de_dispers%C3%A3o).

É muito bom ter o map como referência, porque não importa o tamanho do map, só vai haver uma cópia.

Um conceito que os tipos referência apresentam é que maps podem ser um valor `nil`. Um map `nil` se comporta como um map vazio durante a leitura, mas tentar inserir coisas em um map `nil` gera um panic em tempo de execução. Você pode saber mais sobre maps [aqui](https://blog.golang.org/go-maps-in-action) (em inglês).

Além disso, você nunca deve inicializar um map vazio, como:

```go
var m map[string]string
```

Ao invés disso, você pode inicializar um map vazio como fizemos lá em cima, ou usando a palavra-chave `make` para criar um map para você:

```go
dicionario = map[string]string{}

// OU

dicionario = make(map[string]string)
```

Ambas as abordagens criam um `hash map` vazio e apontam um `dicionario` para ele. Assim, nos certificamos que você nunca vai obter um panic em tempo de execução.

## Refatoração

Não há muito para refatorar na nossa implementação, mas podemos simplificar o teste.

```go
func TestAdiciona(t *testing.T) {
    dicionario := Dicionario{}
    palavra := "teste"
    definicao := "isso é apenas um teste"

    dicionario.Adiciona(palavra, definicao)

    comparaDefinicao(t, dicionario, palavra, definicao)
}

func comparaDefinicao(t *testing.T, dicionario Dicionario, palavra, definicao string) {
    t.Helper()

    resultado, err := dicionario.Busca(palavra)
    if err != nil {
        t.Fatal("deveria ter encontrado palavra adicionada:", err)
    }

    if definicao != resultado {
        t.Errorf("resultado '%s',  esperado '%s'", resultado, definicao)
    }
}
```

Criamos variáveis para palavra e definição e movemos a comparação da definição para sua própria função auxiliar.

Nosso `Adiciona` está bom. No entanto, não consideramos o que acontece quando o valor que estamos tentando adicionar já existe!

O map não vai mostrar um erro se o valor já existe. Ao invés disso, ele vai sobrescrever o valor com o novo recebido. Isso pode ser conveniente na prática, mas torna o nome da nossa função muito menos preciso. `Adiciona` não deve modificar valores existentes. Só deve adicionar palavras novas ao nosso dicionário.

## Escreva o teste primeiro

```go
func TestAdiciona(t *testing.T) {
    t.Run("palavra nova", func(t *testing.T) {
        dicionario := Dicionario{}
        palavra := "teste"
        definicao := "isso é apenas um teste"

        err := dicionario.Adiciona(palavra, definicao)

        comparaErro(t, err, nil)
        comparaDefinicao(t, dicionario, palavra, definicao)
    })

    t.Run("palavra existente", func(t *testing.T) {
        palavra := "teste"
        definicao := "isso é apenas um teste"
        dicionario := Dicionario{palavra: definicao}
        err := dicionario.Adiciona(palavra, "teste novo")

        comparaErro(t, err, ErrPalavraExistente)
        comparaDefinicao(t, dicionario, palavra, definicao)
    })
}
```

Para esse teste, fizemos `Adiciona` devolver um erro, que estamos validando com uma nova variável de erro, `ErrPalavraExistente`. Também modificamos o teste anterior para verificar um erro `nil`.

## Execute o teste

Agora o compilador vai falhar porque não estamos devolvendo um valor para `Adiciona`.

```bash
./dicionario_test.go:30:13: dicionario.Adiciona(palavra, definicao) used as value
./dicionario_test.go:41:13: dicionario.Adiciona(palavra, "teste novo") used as value
```

`usado como valor`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Em `dicionario.go`:

```go
var (
    ErrNaoEncontrado = errors.New("não foi possível encontrar a palavra que você procura")
    ErrPalavraExistente = errors.New("não é possível adicionar a palavra pois ela já existe")
)

func (d Dicionario) Adiciona(palavra, definicao string) error {
    d[palavra] = definicao
    return nil
}
```

Agora temos mais dois erros. Ainda estamos modificando o valor e retornando um erro `nil`.

```bash
dicionario_test.go:43: resultado erro '%!s(<nil>)', esperado 'não é possível adicionar a palavra pois ela já existe'
dicionario_test.go:44: resultado 'teste novo', esperado 'isso é apenas um teste'
```

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Adiciona(palavra, definicao string) error {
    _, err := d.Busca(palavra)
    switch err {
    case ErrNaoEncontrado:
        d[palavra] = definicao
    case nil:
        return ErrPalavraExistente
    default:
        return err

    }

    return nil
}
```

Aqui estamos usando a declaração `switch` para coincidir com o erro. Usar o `switch` dessa forma dá uma segurança a mais, no caso de `Busca` retornar um erro diferente de `ErrNaoEncontrado`.

## Refatoração

Não temos muito o que refatorar, mas já que nossos erros estão aumentando, podemos fazer algumas modificações.

```go
const (
    ErrNaoEncontrado = ErrDicionario("não foi possível encontrar a palavra que você procura")
    ErrPalavraExistente = ErrDicionario("não é possível adicionar a palavra pois ela já existe")
)

type ErrDicionario string

func (e ErrDicionario) Error() string {
    return string(e)
}
```

Tornamos os erros constantes; para isso, tivemos que criar nosso próprio tipo `ErrDicionario` que implementa a interface `error`. Você pode ler mais sobre nesse [artigo excelente escrito por Dave Cheney](https://dave.cheney.net/2016/04/07/constant-errors) (em inglês). Resumindo, isso torna os erros mais reutilizáveis e imutáveis.

Agora, vamos criar uma função que `Atualiza` a definição de uma palavra.

## Escreva o teste primeiro

```go
func TestUpdate(t *testing.T) {
    palavra := "teste"
    definicao := "isso é apenas um teste"
    dicionario := Dicionario{palavra: definicao}
    novaDefinicao := "nova definição"

    dicionario.Atualiza(palavra, novaDefinicao)

    comparaDefinicao(t, dicionario, palavra, novaDefinicao)
}
```

`Atualiza` é bem parecido com `Adiciona` e será nossa próxima implementação.

## Execute o teste

```bash
./dicionario_test.go:53:2: dicionario.Atualiza undefined (type Dicionario has no field or method Atualiza)
```

`dicionario.Atualiza não definido (tipo Dicionario não tem nenhum campo ou método chamado Atualiza`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Já sabemos como lidar com um erro como esse. Precisamos definir nossa função.

```go
func (d Dicionario) Atualiza(palavra, definicao string) {}
```

Feito isso, somos capazes de ver o que precisamos para mudar a definição da palavra.

```bash
dicionario_test.go:55: resultado 'isso é apenas um teste', esperado 'nova definição'
```

## Escreva código o suficiente para fazer o teste passar

Já vimos como fazer essa implementação quando corrigimos o problema com `Adiciona`. Logo, vamos implementar algo bem parecido com `Adiciona`.

```go
func (d Dicionario) Atualiza(palavra, definicao string) {
    d[palavra] = definicao
}
```

Não é necessário fazer refatorar nada, já que foi uma mudança simples. No entanto, agora temos o mesmo problema com `Adiciona`. Se passarmos uma palavra nova, `Atualiza` vai adicioná-la no dicionário.

## Escreva o teste primeiro

```go
    t.Run("palavra existente", func(t *testing.T) {
        palavra := "teste"
        definicao := "isso é apenas um teste"
        novaDefinicao := "nova definição"
        dicionario := Dicionario{palavra: definicao}
        err := dicionario.Atualiza(palavra, novaDefinicao)

        comparaErro(t, err, nil)
        comparaDefinicao(t, dicionario, palavra, novaDefinicao)
    })

    t.Run("palavra nova", func(t *testing.T) {
        palavra := "teste"
        definicao := "isso é apenas um teste"
        dicionario := Dicionario{}

        err := dicionario.Atualiza(palavra, definicao)

        comparaErro(t, err, ErrPalavraInexistente)
    })
```

Criamos um outro tipo de erro para quando a palavra não existe. Também modificamos o `Atualiza` para retornar um valor `error`.

## Execute o teste

```bash
./dicionario_test.go:53:16: dicionario.Atualiza(palavra, "teste novo") used as value
./dicionario_test.go:64:16: dicionario.Atualiza(palavra, definicao) used as value
./dicionario_test.go:66:23: undefined: ErrPalavraInexistente
```

Agora recebemos três erros, mas sabemos como lidar com eles.

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
const (
    ErrNaoEncontrado = ErrDicionario("não foi possível encontrar a palavra que você procura")
    ErrPalavraExistente = ErrDicionario("não é possível adicionar a palavra pois ela já existe")
    ErrPalavraInexistente = ErrDicionario("não foi possível atualizar a palavra pois ela não existe")
)

func (d Dicionario) Atualiza(palavra, definicao string) error {
    d[palavra] = definicao
    return nil
}
```

Adicionamos nosso próprio tipo erro e retornamos um erro `nil`.

Com essas mudanças, agora temos um erro muito mais claro:

```bash
dicionario_test.go:66: resultado erro '%!s(<nil>)', esperado 'não foi possível atualizar a palavra pois ela não existe'
```

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Atualiza(palavra, definicao string) error {
    _, err := d.Busca(palavra)
    switch err {
    case ErrNaoEncontrado:
        return ErrPalavraInexistente
    case nil:
        d[palavra] = definicao
    default:
        return err

    }

    return nil
}
```

Essa função é quase idêntica à `Adiciona`, com exceção de que trocamos quando atualizamos o `dicionario` e quando retornamos um erro.

### Nota sobre a declaração de um novo erro para Atualiza

Poderíamos reutilizar `ErrNaoEncontrado` e não criar um novo erro. No entanto, geralmente é melhor ter um erro preciso para quando uma atualização falhar.

Ter erros específicos te dá mais informação sobre o que deu errado. Segue um exemplo em uma aplicação web:

> Você pode redirecionar o usuário quando o `ErrNaoEncontrado` é encontrado, mas mostrar uma mensagem de erro só quando `ErrPalavraInexistente` é encontrado.

Agora, vamos criar uma função que `Deleta` uma palavra no dicionário.

## Escreva o teste primeiro

```go
func TestDeleta(t *testing.T) {
    palavra := "teste"
    dicionario := Dicionario{palavra: "definição de teste"}

    dicionario.Deleta(palavra)

    _, err := dicionario.Busca(palavra)
    if err != ErrNaoEncontrado {
        t.Errorf("espera-se que '%s' seja deletado", palavra)
    }
}
```

Nosso teste cria um `Dicionario` com uma palavra e depois verifica se a palavra foi removida.

## Execute o teste

Executando `go test` obtemos:

```bash
./dicionario_test.go:74:6: dicionario.Deleta undefined (type Dicionario has no field or method Deleta)
```

`dicionario.Deleta não definido (tipo Dicionario não tem campo ou método Deleta)`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (d Dicionario) Deleta(palavra string) {

}
```

Depois que adicionamos isso, o teste nos diz que não estamos deletando a palavra.

```bash
dicionario_test.go:78: espera-se que 'teste' seja deletado
```

## Escreva código o suficiente para fazer o teste passar

```go
func (d Dicionario) Deleta(palavra string) {
    delete(d, palavra)
}
```

Go tem uma função nativa chamada `delete` que funciona em maps. Ela leva dois argumentos: o primeiro é o map e o segundo é a chave a ser removida.

A função `delete` não retorna nada, e baseamos nosso método `Deleta` nesse conceito. Já que deletar um valor não tem nenhum efeito, diferentemente dos nossos métodos `Atualiza` e `Adiciona`, não precisamos complicar a API com erros.

## Resumo

Nessa seção, falamos sobre muita coisa. Criamos uma API CRUD (Criar, Ler, Atualizar e Deletar) completa para nosso dicionário. No decorrer do processo, aprendemos como:

* Criar maps
* Buscar por itens em maps
* Adicionar novos itens aos maps
* Atualizar itens em maps
* Deletar itens de um map
* Aprendemos mais sobre erros
  * Como criar erros que são constantes
  * Escrever encapsuladores de erro


# Injeção de dependência

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/injecao-de-dependencia)

Presume-se que você tenha lido a seção de `structs` antes, já que será necessário saber um pouco sobre interfaces para entender este capítulo.

Há muitos mal entendidos relacionados à injeção de dependência na comunidade de programação. Se tudo der certo, esse guia vai te mostrar que:

* Você não precisa de uma framework
* Não torna seu design complexo demais
* Facilita seus testes
* Permite que você escreva funções ótimas para propósitos diversos.

Queremos criar uma função que cumprimenta alguém, assim como a que fizemos no capítulo [Olá, mundo](/aprenda-go-com-testes/primeiros-passos-com-go/ola-mundo), mas dessa vez vamos testar o *print de verdade*.

Para recapitular, a função era parecida com isso:

```go
func Cumprimenta(nome string) {
    fmt.Printf("Olá, %s", nome)
}
```

Mas como podemos testar isso? Chamar `fmt.Printf` imprime na saída, o que torna a captura com a ferramenta de testes bem difícil para nós.

O que precisamos fazer é sermos capazes de **injetar** (que é só uma palavra chique para passar) a dependência de impressão.

**Nossa função não precisa se preocupar com** ***onde*** **ou** ***como*** **a impressão acontece, então vamos aceitar uma** ***interface*** **ao invés de um tipo concreto.**

Se fizermos isso, podemos mudar a implementação para imprimir algo que controlamos para poder testá-lo. Na "vida real", você iria injetar em algo que escreve na saída.

Se dermos uma olhada no código fonte do `fmt.Printf`, podemos ver uma forma de começar:

```go
// Printf retorna o número de bytes escritos e algum erro de escrita encontrado.
func Printf(format string, a ...interface{}) (n int, err error) {
    return Fprintf(os.Stdout, format, a...)
}
```

Interessante! Por baixo dos panos, o `Printf` só chama o `Fprintf` passando o `os.Stdout`.

O que exatamente *é* um `os.Stdout`? O que o `Fprintf` espera que passe para ele como primeiro argumento?

```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
}
```

Um `io.Writer`:

```go
type Writer interface {
    Write(p []byte) (n int, err error)
}
```

Quanto mais você escreve código em Go, mais vai perceber que essa interface aparece bastante, pois é uma ótima interface de uso geral para "colocar esses dados em algum lugar".

Logo, sabemos que por baixo dos panos estamos usando o `Writer` para enviar nosso cumprimento para algum lugar. Vamos usar essa abstração existente para tornar nosso código testável e mais reutilizável.

## Escreva o teste primeiro

```go
func TestCumprimenta(t *testing.T) {
    buffer := bytes.Buffer{}
    Cumprimenta(&buffer, "Chris")

    resultado := buffer.String()
    esperado := "Olá, Chris"

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

O tipo `buffer` do pacote `bytes` implementa a interface `Writer`.

Logo, vamos utilizá-lo no nosso teste para enviá-lo como nosso `Writer` e depois podemos verificar o que foi escrito nele quando chamamos `Cumprimenta`.

## Execute o teste

O teste não vai compilar:

```bash
./id_test.go:10:7: too many arguments in call to Cumprimenta
    have (*bytes.Buffer, string)
    want (string)
```

```bash
./id_test.go:10:7: muitos argumentos na chamada de Cumprimenta
    obteve (*bytes.Buffer, string)
    esperado (string)
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

*Preste atenção no compilador* e corrija o problema.

```go
func Cumprimenta(escritor io.Writer, nome string) {
    fmt.Printf("Olá, %s", nome)
}
```

`Olá, Chris id_test.go:16: resultado '', esperado 'Olá, Chris'`

O teste falha. Note que o nome está sendo impresso, mas está indo para a saída.

## Escreva código o suficiente para fazer o teste passar

Use o escritor para enviar o cumprimento para o buffer no nosso teste. Lembre-se que o `fmt.Fprintf` é parecido com o `fmt.Printf`, com a diferença de que leva um `Writer` em que a string é enviada, enquanto que o`fmt.Printf` redireciona para a saída por padrão.

```go
func Cumprimenta(escritor io.Writer, nome string) {
    fmt.Fprintf(escritor, "Olá, %s", nome)
}
```

Agora o teste vai passar.

## Refatoração

Antes, o compilador nos disse para passar um ponteiro para um `bytes.Buffer`. Isso está tecnicamente correto, mas não é muito útil.

Para demonstrar isso, tente utilizar a função `Cumprimenta` em uma aplicação Go onde queremos que imprima na saída.

```go
func main() {
    Cumprimenta(os.Stdout, "Elodie")
}
```

`./id.go:14:7: cannot use os.Stdout (type *os.File) as type *bytes.Buffer in argument to Cumprimenta`

`não é possível utilizar os.Stdout (tipo *os.File) como tipo *bytes.Buffer no argumento para Cumprimenta`

Como discutimos antes, o `fmt.Fprintf` te permite passar um `io.Writer`, que sabemos que o `os.Stdout` e `bytes.Buffer` implementam.

Se mudarmos nosso código para usar uma interface de propósito mais geral, podemos usá-la tanto nos testes quanto na nossa aplicação.

```go
package main

import (
    "fmt"
    "os"
    "io"
)

func Cumprimenta(escritor io.Writer, nome string) {
    fmt.Fprintf(escritor, "Olá, %s", nome)
}

func main() {
    Cumprimenta(os.Stdout, "Elodie")
}
```

## Mais sobre io.Writer

Quais outros lugares podemos escrever dados usando `io.Writer`? Para qual propósito geral nossa função `Cumprimenta` é feita?

### A internet

Execute o seguinte:

```go
package main

import (
    "fmt"
    "io"
    "net/http"
)

func Cumprimenta(escritor io.Writer, nome string) {
    fmt.Fprintf(escritor, "Olá, %s", nome)
}

func HandlerMeuCumprimento(w http.ResponseWriter, r *http.Request) {
    Cumprimenta(w, "mundo")
}

func main() {
    err := http.ListenAndServe(":5000", http.HandlerFunc(HandlerMeuCumprimento))

    if err != nil {
        fmt.Println(err)
    }
}
```

Execute o programa e vá para <http://localhost:5000>. Você verá sua função de cumprimento ser utilizada.

Falaremos sobre servidores HTTP em um próximo capítulo, então não se preocupe muito com os detalhes.

Quando se cria um handler HTTP, você recebe um `http.ResponseWriter` e o `http.Request` que é usado para fazer a requisição. Quando implementa seu servidor, você *escreve* sua resposta usando o escritor.

Você deve ter adivinhado que o `http.ResponseWriter` também implementa o `io.Writer` e é por isso que podemos reutilizar nossa função `Cumprimenta` dentro do nosso handler.

## Resumo

Nossa primeira rodada de código não foi fácil de testar porque escrevemos dados em algum lugar que não podíamos controlar.

*Graças aos nossos testes*, refatoramos o código para que pudéssemos controlar para *onde* os dados eram escritos **injetando uma dependência** que nos permitiu:

* **Testar nosso código**: se você não consegue testar uma função *de forma simples*, geralmente é porque dependências estão acopladas em uma função *ou* estado global. Se você tem um pool de conexão global da base de dados, por exemplo, é provável que seja difícil testar e vai ser lento para ser execudado. A injeção de dependência te motiva a injetar em uma dependência da base de dados (através de uma interface), para que você possa criar um mock com algo que você possa controlar nos seus testes.
* *Separar nossas preocupações*, desacoplando *onde os dados vão* de *como gerá-los*. Se você já achou que um método/função tem responsabilidades demais (gerando dados *e* escrevendo na base de dados? Lidando com requisições HTTP *e* aplicando lógica a nível de domínio?), a injeção de dependência provavelmente será a ferramenta que você precisa.
* **Permitir que nosso código seja reutilizado em contextos diferentes**: o primeiro contexto "novo" do nosso código pode ser usado dentro dos testes. No entanto, se alguém quiser testar algo novo com nossa função, a pessoa pode injetar suas próprias dependências.

### Mas e o mock? Ouvi falar que precisa disso para trabalhar com injeção de dependência e que também é do demonho

Vamos falar mais sobre mocks depois (e não é do demonho). Você mocka para substituir coisas reais que você injeta com uma versão falsa que você pode controlar e examinar nos seus testes. No entanto, no nosso caso a biblioteca padrão já tinha algo pronto para usarmos.

### A biblioteca padrão do Go é muito boa, leve um tempo para estudá-la

Ao termos familiaridade com a interface `io.Writer`, somos capazes de usar `bytes.Buffer` no nosso teste como nosso `Writer` para que depois possamos usar outros `Writer` da biblioteca padrão para usar na nossa função em uma aplicação de linha de comando ou em um servidor web.

Quanto mais familiar você for com a biblioteca padrão, mais vai ver essas interfaces de propósito geral que você pode reutilizar no seu próprio código para tornar o software reutilizável em vários contextos diferentes.

Esse exemplo teve grande influência de um capítulo de [A Linguagem de Programação Go](https://www.amazon.com.br/dp/8575225464/ref=cm_sw_r_tw_dp_U_x_0HIqDbYP7VSN5). Logo, se gostou, vá adquiri-lo!


# Mocks

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/mocks)

Te pediram para criar um programa que conta a partir de 3, imprimindo cada número em uma linha nova (com um segundo de intervalo entre cada uma) e quando chega a zero, imprime "Vai!" e sai.

```
3
2
1
Vai!
```

Vamos resolver isso escrevendo uma função chamada `Contagem` que vamos colocar dentro de um programa `main` e se parecer com algo assim:

```go
package main

func main() {
    Contagem()
}
```

Apesar de ser um programa simples, para testá-lo completamente vamos precisar, como de costume, de uma abordagem *iterativa* e *orientada a testes*.

Mas o que quero dizer com iterativa? Precisamos ter certeza de que tomamos os menores passos que pudermos para ter um *software* útil.

Não queremos passar muito tempo com código que vai funcionar hora ou outra após alguma implementação mirabolante, porque é assim que os desenvolvedores caem em armadilhas. **É importante ser capaz de dividir os requerimentos da menor forma que conseguir para você ter um** ***software funcionando*****.**

Podemos separar essa tarefa da seguinte forma:

* Imprimir 3
* Imprimir de 3 para Vai!
* Esperar um segundo entre cada linha

## Escreva o teste primeiro

Nosso software precisa imprimir para a saída. Vimos como podemos usar a injeção de dependência para facilitar nosso teste na [seção anterior](/aprenda-go-com-testes/primeiros-passos-com-go/injecao-de-dependencia).

```go
func TestContagem(t *testing.T) {
    buffer := &bytes.Buffer{}

    Contagem(buffer)

    resultado := buffer.String()
    esperado := "3"

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

Se tiver dúvidas sobre o `buffer`, leia a [seção anterior](/aprenda-go-com-testes/primeiros-passos-com-go/injecao-de-dependencia) novamente.

Sabemos que nossa função `Contagem` precisa escrever dados em algum lugar e o `io.Writer` é a forma de capturarmos essa saída como uma interface em Go.

* Na `main`, vamos enviar o `os.Stdout` como parâmetro para nossos usuários verem a contagem regressiva impressa no terminal.
* No teste, vamos enviar o `bytes.Buffer` como parâmetro para que nossos testes possam capturar que dado está sendo gerado.

## Execute o teste

`./contagem_test.go:11:2: undefined: Contagem`

`indefinido: Contagem`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Defina `Contagem`:

```go
func Contagem() {}
```

Tente novamente:

```go
./contagem_test.go:11:11: too many arguments in call to Countdown
    have (*bytes.Buffer)
    want ()
```

`argumentos demais na chamada para Contagem`

O compilador está te dizendo como a assinatura da função deve ser, então é só atualizá-la.

```go
func Contagem(saida *bytes.Buffer) {}
```

`contagem_test.go:17: resultado '', esperado '3'`

Perfeito!

## Escreva código o suficiente para fazer o teste passar

```go
func Contagem(saida *bytes.Buffer) {
    fmt.Fprint(saida, "3")
}
```

Estamos usando `fmt.Fprint`, o que significa que ele recebe um `io.Writer` (como `*bytes.Buffer`) e envia uma `string` para ele. O teste deve passar.

## Refatoração

Agora sabemos que, apesar do `*bytes.Buffer` funcionar, seria melhor ter uma interface de propósito geral ao invés disso.

```go
func Contagem(saida io.Writer) {
    fmt.Fprint(saida, "3")
}
```

Execute os testes novamente e eles devem passar.

Só para finalizar, vamos colocar nossa função dentro da `main` para que possamos executar o software para nos assegurarmos de que estamos progredindo.

```go
package main

import (
    "fmt"
    "io"
    "os"
)

func Contagem(saida io.Writer) {
    fmt.Fprint(saida, "3")
}

func main() {
    Contagem(os.Stdout)
}
```

Execute o programa e surpreenda-se com seu trabalho.

Apesar de parecer simples, essa é a abordagem que recomendo para qualquer projeto. **Escolher uma pequena parte da funcionalidade e fazê-la funcionar do começo ao fim com apoio de testes.**

Depois, precisamos fazer o software imprimir 2, 1 e então "Vai!".

## Escreva o teste primeiro

Após investirmos tempo e esforço para fazer o principal funcionar, podemos iterar nossa solução com segurança e de forma simples. Não vamos mais precisar parar e executar o programa novamente para ter confiança de que ele está funcionando, desde que a lógica esteja testada.

```go
func TestContagem(t *testing.T) {
    buffer := &bytes.Buffer{}

    Contagem(buffer)

    resultado := buffer.String()
    esperado := `3
2
1
Vai!`
    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

A sintaxe de aspas simples é outra forma de criar uma `string`, mas te permite colocar coisas como linhas novas, o que é perfeito para nosso teste.

## Execute o teste

```bash
contagem_test.go:21: resultado '3', esperado '3
        2
        1
        Vai!'
```

## Escreva código o suficiente para fazer o teste passar

```go
func Contagem(saida io.Writer) {
    for i := 3; i > 0; i-- {
        fmt.Fprintln(saida, i)
    }
    fmt.Fprint(saida, "Go!")
}
```

Usamos um laço `for` fazendo contagem regressiva com `i--` e depois `fmt.Fprintln` para imprimir a `saida` com nosso número seguro por um caracter de nova linha. Finalmente, usamos o `fmt.Fprint` para enviar "Vai!" no final.

## Refatoração

Não há muito para refatorar além de transformar alguns valores mágicos em constantes com nomes descritivos.

```go
const ultimaPalavra = "Go!"
const inicioContagem = 3

func Contagem(saida io.Writer) {
    for i := inicioContagem; i > 0; i-- {
        fmt.Fprintln(saida, i)
    }
    fmt.Fprint(saida, ultimaPalavra)
}
```

Se executar o programa agora, você deve obter a saída desejada, mas não tem uma contagem regressiva dramática com as pausas de 1 segundo.

Go te permite obter isso com `time.Sleep`. Tente adicionar essa função ao seu código.

```go
func Contagem(saida io.Writer) {
    for i := inicioContagem; i > 0; i-- {
        time.Sleep(1 * time.Second)
        fmt.Fprintln(saida, i)
    }

    time.Sleep(1 * time.Second)
    fmt.Fprint(saida, ultimaPalavra)
}
```

Se você executar o programa, ele funciona conforme esperado.

## Mock

Os testes ainda vão passar e o software funciona como planejado, mas temos alguns problemas:

* Nossos testes levam 4 segundos para rodar.
  * Todo conteúdo gerado sobre desenvolvimento de software enfatiza a importância de loops de feedback rápidos.
  * **Testes lentos arruinam a produtividade do desenvolvedor**.
  * Imagine se os requerimentos ficam mais sofisticados, gerando a necessidade de mais testes. É viável adicionar 4s para cada teste novo de `Contagem`?
* Não testamos uma propriedade importante da nossa função.

Temos uma dependência no `Sleep` que precisamos extrair para podermos controlá-la nos nossos testes.

Se conseguirmos *mockar* o `time.Sleep`, podemos usar a *injeção de dependências* para usá-lo ao invés de um `time.Sleep` "de verdade", e então podemos **verificar as chamadas** para certificar de que estão corretas.

## Escreva o teste primeiro

Vamos definir nossa dependência como uma interface. Isso nos permite usar um Sleeper *de verdade* em `main` e um *sleeper spy* nos nossos testes. Usar uma interface na nossa função `Contagem` é essencial para isso e dá certa flexibilidade à função que a chamar.

```go
type Sleeper interface {
    Sleep()
}
```

Tomei uma decisão de design que nossa função `Contagem` não seria responsável por quanto tempo o sleep leva. Isso simplifica um pouco nosso código, pelo menos por enquanto, e significa que um usuário da nossa função pode configurar a duração desse tempo como preferir.

Agora precisamos criar um *mock* disso para usarmos nos nossos testes.

```go
type SleeperSpy struct {
    Chamadas int
}

func (s *SleeperSpy) Sleep() {
    s.Chamadas++
}
```

*Spies* (espiões) são um tipo de *mock* em que podemos gravar como uma dependência é usada. Eles podem gravar os argumentos definidos, quantas vezes são usados etc. No nosso caso, vamos manter o controle de quantas vezes `Sleep()` é chamada para verificá-la no nosso teste.

Atualize os testes para injetar uma dependência no nosso Espião e verifique se o sleep foi chamado 4 vezes.

```go
func TestContagem(t *testing.T) {
    buffer := &bytes.Buffer{}
    sleeperSpy := &SleeperSpy{}

    Contagem(buffer, sleeperSpy)

    resultado := buffer.String()
    esperado := `3
2
1
Vai!`

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }

    if sleeperSpy.Chamadas != 4 {
        t.Errorf("não houve chamadas suficientes do sleeper, esperado 4, resultado %d", sleeperSpy.Chamadas)
    }
}
```

## Execute o teste

```bash
too many arguments in call to Contagem
    have (*bytes.Buffer, *SpySleeper)
    want (io.Writer)
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Precisamos atualizar a `Contagem` para aceitar nosso `Sleeper`:

```go
func Contagem(saida io.Writer, sleeper Sleeper) {
    for i := inicioContagem; i > 0; i-- {
        time.Sleep(1 * time.Second)
        fmt.Fprintln(saida, i)
    }

    time.Sleep(1 * time.Second)
    fmt.Fprint(saida, ultimaPalavra)
}
```

Se tentar novamente, nossa `main` não vai mais compilar pelo mesmo motivo:

```
./main.go:26:11: not enough arguments in call to Contagem
    have (*os.File)
    want (io.Writer, Sleeper)
```

Vamos criar um sleeper *de verdade* que implementa a interface que precisamos:

```go
type SleeperPadrao struct {}

func (d *SleeperPadrao) Sleep() {
    time.Sleep(1 * time.Second)
}
```

Podemos usá-lo na nossa aplicação real, como:

```go
func main() {
    sleeper := &SleeperPadrao{}
    Contagem(os.Stdout, sleeper)
}
```

## Escreva código o suficiente para fazer o teste passar

Agora o teste está compilando, mas não passando. Isso acontece porque ainda estamos chamando o `time.Sleep` ao invés da injetada. Vamos arrumar isso.

```go
func Contagem(saida io.Writer, sleeper Sleeper) {
    for i := inicioContagem; i > 0; i-- {
        sleeper.Sleep()
        fmt.Fprintln(saida, i)
    }

    sleeper.Sleep()
    fmt.Fprint(saida, ultimaPalavra)
}
```

O teste deve passar sem levar 4 segundos.

### Ainda temos alguns problemas

Ainda há outra propriedade importante que não estamos testando.

A `Contagem` deve ter uma pausa para cada impressão, como por exemplo:

* `Pausa`
* `Imprime N`
* `Pausa`
* `Imprime N-1`
* `Pausa`
* `Imprime Vai!`
* etc

Nossa alteração mais recente só verifica se o software teve 4 pausas, mas essas pausas poderiam ocorrer fora de ordem.

Quando escrevemos testes, se não estiver confiante de que seus testes estão te dando confiança o suficiente, quebre-o (mas certifique-se de que você salvou suas alterações antes)! Mude o código para o seguinte:

```go
func Contagem(saida io.Writer, sleeper Sleeper) {
    for i := inicioContagem; i > 0; i-- {
        sleeper.Pausa()
        fmt.Fprintln(saida, i)
    }

    for i := inicioContagem; i > 0; i-- {
        fmt.Fprintln(saida, i)
    }

    sleeper.Pausa()
    fmt.Fprint(saida, ultimaPalavra)
}
```

Se executar seus testes, eles ainda vão passar, apesar da implementação estar errada.

Vamos usar o spy novamente com um novo teste para verificar se a ordem das operações está correta.

Temos duas dependências diferentes e queremos gravar todas as operações delas em uma lista. Logo, vamos criar *um spy para ambas*.

```go
type SpyContagemOperacoes struct {
    Chamadas []string
}

func (s *SpyContagemOperacoes) Pausa() {
    s.Chamadas = append(s.Chamadas, pausa)
}

func (s *SpyContagemOperacoes) Write(p []byte) (n int, err error) {
    s.Chamadas = append(s.Chamadas, escrita)
    return
}

const escrita = "escrita"
const pausa = "pausa"
```

Nosso `SpyContagemOperacoes` implementa tanto o `io.Writer` quanto o `Sleeper`, gravando cada chamada em um slice. Nesse teste, temos preocupação apenas na ordem das operações, então apenas gravá-las em uma lista de operações nomeadas é suficiente.

Agora podemos adicionar um subteste no nosso conjunto de testes.

```go
t.Run("pausa antes de cada impressão", func(t *testing.T) {
        spyImpressoraSleep := &SpyContagemOperacoes{}
        Contagem(spyImpressoraSleep, spyImpressoraSleep)

        esperado := []string{
            pausa,
            escrita,
            pausa,
            escrita,
            pausa,
            escrita,
            pausa,
            escrita,
        }

        if !reflect.DeepEqual(esperado, spyImpressoraSleep.Chamadas) {
            t.Errorf("esperado %v chamadas, resultado %v", esperado, spyImpressoraSleep.Chamadas)
        }
    })
```

Esse teste deve falhar. Volte o código que quebramos para a versão correta e agora o novo teste deve passar.

Agora temos dois spies no `Sleeper`. O próximo passo é refatorar nosso teste para que um teste o que está sendo impresso e o outro se certifique de que estamos pausando entre as impressões. Por fim, podemos apagar nosso primeiro spy, já que não é mais utilizado.

```go
func TestContagem(t *testing.T) {

    t.Run("imprime 3 até Vai!", func(t *testing.T) {
        buffer := &bytes.Buffer{}
        Contagem(buffer, &SpyContagemOperacoes{})

        resultado := buffer.String()
        esperado := `3
2
1
Vai!`

        if resultado != esperado {
            t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
        }
    })

    t.Run("pausa antes de cada impressão", func(t *testing.T) {
        spyImpressoraSleep := &SpyContagemOperacoes{}
        Contagem(spyImpressoraSleep, spyImpressoraSleep)

        esperado := []string{
            pausa,
            escrita,
            pausa,
            escrita,
            pausa,
            escrita,
            pausa,
            escrita,
        }

        if !reflect.DeepEqual(esperado, spyImpressoraSleep.Chamadas) {
            t.Errorf("esperado %v chamadas, resultado %v", esperado, spyImpressoraSleep.Chamadas)
        }
    })
}
```

Agora temos nossa função e suas duas propriedades testadas adequadamente.

## Extendendo o Sleeper para se tornar configurável

Uma funcionalidade legal seria o `Sleeper` ser configurável.

### Escreva o teste primeiro

Agora vamos criar um novo tipo para `SleeperConfiguravel` que aceita o que precisamos para configuração e teste.

```go
type SleeperConfiguravel struct {
    duracao time.Duration
    pausa   func(time.Duration)
}
```

Estamos usando a `duracao` para configurar o tempo de pausa e `pausa` como forma de passar uma função de pausa. A assinatura de `sleep` é a mesma de `time.Sleep`, nos permitindo usar `time.Sleep` na nossa implementação real e um spy nos nossos testes.

```go
type TempoSpy struct {
    duracaoPausa time.Duration
}

func (t *TempoSpy) Pausa(duracao time.Duration) {
    t.duracaoPausa = duracao
}
```

Definindo nosso spy, podemos criar um novo teste para o sleeper configurável.

```go
func TestSleeperConfiguravel(t *testing.T) {
    tempoPausa := 5 * time.Second

    tempoSpy := &TempoSpy{}
    sleeper := SleeperConfiguravel{tempoPausa, tempoSpy.Pausa}
    sleeper.Pausa()

    if tempoSpy.duracaoPausa != tempoPausa {
        t.Errorf("deveria ter pausado por %v, mas pausou por %v", tempoPausa, tempoSpy.duracaoPausa)
    }
}
```

Não há nada de novo nesse teste e seu funcionamento é bem semelhante aos testes com mock anteriores.

### Execute o teste

```bash
sleeper.Pausa undefined (type SleeperConfiguravel has no field or method Pausa, but does have pausa)
```

`sleeper.Pausa não definido (tipo SleeperConfiguravel não tem campo ou método Pausa, mas tem o método sleep`

Você deve ver uma mensagem de erro bem clara indicando que não temos um método `Pausa` criado no nosso `SleeperConfiguravel`.

### Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func (c *SleeperConfiguravel) Pausa() {
}
```

Com nossa nova função `Pausa` implementada, ainda há um teste falhando.

```bash
contagem_test.go:56: deveria ter pausado por 5s, mas pausou por 0s
```

### Escreva código o suficiente para fazer o teste passar

Tudo o que precisamos fazer agora é implementar a função `Pausa` para o `SleeperConfiguravel`.

```go
func (s *SleeperConfiguravel) Pausa() {
    s.pausa(s.duracao)
}
```

Com essa mudança, todos os testes devem voltar a passar.

### Limpeza e refatoração

A última coisa que precisamos fazer é de fato usar nosso `SleeperConfiguravel` na função main.

```go
func main() {
    sleeper := &SleeperConfiguravel{1 * time.Second, time.Sleep}
    Contagem(os.Stdout, sleeper)
}
```

Se executarmos os testes e o programa manualmente, podemos ver que todo o comportamento permanece o mesmo.

Já que estamos usando o `SleeperConfiguravel`, é seguro deletar o `SleeperPadrao`.

## Mas o mock não é do demonho?

Você já deve ter ouvido que o mock é do mal. Quase qualquer coisa no desenvolvimento de software pode ser usada para o mal, assim como o [DRY](https://pt.wikipedia.org/wiki/Don%27t_repeat_yourself).

As pessoas acabam chegando numa fase ruim em que não *dão atenção aos próprios testes* e *não respeitam a etapa de refatoração*.

Se seu código de mock estiver ficando complicado ou você tem que mockar muita coisa para testar algo, você deve *prestar mais atenção* a essa sensação ruim e pensar sobre o seu código. Geralmente isso é sinal de que:

* A coisa que você está testando está tendo que fazer coisas demais
  * Modularize a função para que faça menos coisas
* Suas dependências estão muito desacopladas
  * Pense e uma forma de consolidar algumas das dependências em um módulo útil
* Você está se preocupando demais com detalhes de implementação
  * Dê prioridade em testar o comportamento esperado ao invés da implementação

Normalmente, muitos pontos de mock são sinais de *abstração ruim* no seu código.

**As pessoas costumam pensar que essa é uma fraqueza no TDD, mas na verdade é um ponto forte**. Testes mal desenvolvidos são resultado de código ruim. Código bem desenvolvido é fácil de ser testado.

### Só que mocks e testes ainda estão dificultando minha vida!

Já se deparou com a situação a seguir?

* Você quer refatorar algo
* Para isso, você precisa mudar vários testes
* Você duvida do TDD e cria um post no Medium chamado "Mock é prejudicial"

Isso costuma ser um sinal de que você está testando muito *detalhe de implementação*. Tente fazer de forma que esteja testando *comportamentos úteis*, a não ser que a implementação seja tão importante que a falta dela possa fazer o sistema quebrar.

Às vezes é difícil saber *qual nível* testar exatamente, então aqui vai algumas ideias e regras que tento seguir:

-**A definição de refatoração é que o código muda, mas o comportamento permanece o mesmo**. Se você decidiu refatorar alguma coisa, na teoria você deve ser capaz de salvar seu código sem que o teste mude. Então, quando estiver escrevendo um teste, pergunte para si: - Estou testando o comportamento que quero ou detalhes de implementação? - Se fosse refatorar esse código, eu teria que fazer muitas mudanças no meu teste?

* Apesar do Go te deixar testar funções privadas, eu evitaria fazer isso, já que funções privadas costumam ser detalhes de implementação.
* Se o teste estiver com **3 mocks, esse é um sinal de alerta** - hora de repensar no design.
* Use spies com cuidado. Spies te deixam ver a parte interna do algoritmo que você está escrevendo, o que pode ser bem útil, mas significa que há um acoplamento maior entre o código do teste e a implementação. **Certifique-se de que você realmente precisa desses detalhes se você vai colocar um spy neles**.

Como sempre, regras no desenvolvimento de software não são realmente regras e podem haver exceções. [O artigo do Uncle Bob sobre "Quando mockar"](https://8thlight.com/blog/uncle-bob/2014/05/10/WhenToMock.html) (em inglês) tem alguns pontos excelentes.

## Resumo

### Mais sobre abordagem TDD

* Quando se deparar com exemplos menos comuns, divida o problema em "linhas verticais finas". Tente chegar em um ponto onde você tem *software em funcionamento com o apoio de testes* o mais rápido possível, para evitar cair em armadilhas e se perder.
* Quando tiver uma parte do software em funcionamento, deve ser mais fácil *iterar com etapas pequenas* até chegar no software que você precisa.

> "Quando usar o desenvolvimento iterativo? Apenas em projetos que você quer obter sucesso."

Martin Fowler.

### Mock

* **Sem o mock, partes importantes do seu código não serão testadas**. No nosso caso, não seríamos capazes de testar se nosso código pausava em cada impressão, mas existem inúmeros exemplos. Chamar um serviço que *pode* falhar? Querer testar seu sistema em um estado em particular? É bem difícil testar esses casos sem mock.
* Sem mocks você pode ter que definir bancos de dados e outras dependências externas só para testar regras de negócio simples. Seus testes provavelmente ficarão mais lentos, resultando em **loops de feedback lentos**.
* Ter que se conectar a um banco de dados ou webservice para testar algo vai tornar seus testes **frágeis** por causa da falta de segurança nesses serviços.

Uma vez que a pessoa aprende a mockar, é bem fácil testar pontos demais de um sistema em termos da *forma que ele funciona* ao invés *do que ele faz*. Sempre tenha em mente o **valor dos seus testes** e qual impacto eles teriam em uma refatoração futura.

Nesse artigo sobre mock, falamos sobre **spies**, que são um tipo de mock. Aqui estão diferentes tipos de mocks. [O Uncle Bob explica os tipos em um artigo bem fácil de ler](https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html) (em inglês). Nos próximos capítulos, vamos precisar escrever código que depende de outros para obter dados, que é aonde vou mostrar os **Stubs** em ação.


# Concorrência

[**Você pode encontrar todos os códigos para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/concorrencia)

A questão é a seguinte: um colega escreveu uma função, `VerificaWebsites`, que verifica o status de uma lista de URLs.

```go
package concorrencia

type VerificadorWebsite func(string) bool

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)

    for _, url := range urls {
        resultados[url] = vw(url)
    }

    return resultados
}
```

Ela retorna um map de cada URL verificado com um valor booleano - `true` para uma boa resposta, `false` para uma resposta ruim.

Você também tem que passar um `VerificadorWebsite` como parâmetro, que leva um URL e retorna um boleano. Isso é usado pela função que verifica todos os websites.

Usando a [injeção de dependência](/aprenda-go-com-testes/primeiros-passos-com-go/injecao-de-dependencia), conseguimos testar a função sem fazer chamadas HTTP de verdade, tornando o teste seguro e rápido.

Aqui está o teste que escreveram:

```go
package concurrency

import (
    "reflect"
    "testing"
)

func mockVerificadorWebsite(url string) bool {
    if url == "waat://furhurterwe.geds" {
        return false
    }
    return true
}

func TestVerificaWebsites(t *testing.T) {
    websites := []string{
        "http://google.com",
        "http://blog.gypsydave5.com",
        "waat://furhurterwe.geds",
    }

    esperado := map[string]bool{
        "http://google.com":          true,
        "http://blog.gypsydave5.com": true,
        "waat://furhurterwe.geds":    false,
    }

    resultado := VerificaWebsites(mockVerificadorWebsite, websites)

    if !reflect.DeepEqual(esperado, resultado) {
        t.Fatalf("esperado %v, resultado %v", esperado, resultado)
    }
}
```

A função que está em produção está sendo usada para verificar centenas de websites. Só que seu colega começou a reclamar que está lento demais, e pediram sua ajuda para melhorar a velocidade dele.

## Escreva o teste primeiro

Vamos usar um teste de benchmark para testar a velocidade de `VerificaWebsites` para que possamos ver o efeito das nossas alterações.

```go
package concorrencia

import (
    "testing"
    "time"
)

func slowStubVerificadorWebsite(_ string) bool {
    time.Sleep(20 * time.Millisecond)
    return true
}

func BenchmarkVerificaWebsites(b *testing.B) {
    urls := make([]string, 100)
    for i := 0; i < len(urls); i++ {
        urls[i] = "uma url"
    }

    for i := 0; i < b.N; i++ {
        VerificaWebsites(slowStubVerificadorWebsite, urls)
    }
}
```

O benchmark testa `VerificaWebsites` usando um slice de 100 URLs e usa uma nova implementação falsa de `VerificadorWebsite`. `slowStubVerificadorWebsite` é intencionalmente lento. Ele usa um `time.Sleep` para esperar exatamente 20 milissegundos e então retorna verdadeiro.

Quando executamos o benchmark com `go test -bench=.` (ou, se estiver no Powershell do Windows, `go test -bench="."`):

```bash
pkg: github.com/larien/aprenda-go-com-testes/concorrencia/v1
BenchmarkVerificaWebsites-4               1        2249228637 ns/op
PASS
ok      github.com/larien/aprenda-go-com-testes/concorrencia/v1        2.268s
```

`VerificaWebsites` teve uma marca de 2249228637 nanosegundos - pouco mais de dois segundos.

Vamos torná-lo mais rápido.

### Escreva código o suficiente para fazer o teste passar

Agora finalmente podemos falar sobre concorrência que, apenas para fins dessa situação, significa "fazer mais do que uma coisa ao mesmo tempo". Isso é algo que fazemos naturalmente todo dia.

Por exemplo, hoje de manhã fiz uma xícara de chá. Coloquei a chaleira no fogo e, enquanto esperava a água ferver, tirei o leite da geladeira, tirei o chá do armário, encontrei minha xícara favorita, coloquei o saquinho do chá e, quando a chaleira ferveu a água, coloquei a água na xícara.

O que eu *não fiz* foi colocar a chaleira no fogo e então ficar sem fazer nada só esperando a chaleira ferver a água, para depois fazer todo o restante quando a água tivesse fervido.

Se conseguir entender por que é mais rápido fazer chá da primeira forma, então você é capaz de entender como vamos tornar o `VerificaWebsites` mais rápido. Ao invés de esperar por um website responder antes de enviar uma requisição para o próximo website, vamos dizer para nosso computador fazer a próxima requisição enquanto espera pela primeira.

Normalmente, em Go, quando chamamos uma função `fazAlgumaCoisa()`, esperamos que ela retorne alguma coisa (mesmo se não tiver valor para retornar, ainda esperamos que ela termine). Chamamos essa operação de *bloqueante* - espera algo acabar para terminar seu trabalho. Uma operação que não bloqueia no Go vai rodar em um *processo* separado, chamado de *goroutine*. Pense no processo como uma leitura de uma página de código Go de cima para baixo, 'entrando' em cada função quando é chamado para ler o que essa página faz. Quando um processo separado começa, é como se outro leitor começasse a ler o interior da função, deixando o leitor original continuar lendo a página.

Para dizer ao Go começar uma nova goroutine, transformamos a chamada de função em uma declaração `go` colocando a palavra-chave `go` na frente da função: `go fazAlgumaCoisa()`.

```go
package concurrency

type VerificadorWebsite func(string) bool

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)

    for _, url := range urls {
        go func() {
            resultados[url] = vw(url)
        }()
    }

    return resultados
}
```

Já que a única forma de começar uma goroutine é colocar `go` na frente da chamada de função, costumamos usar *funções anônimas* quando queremos iniciar uma goroutine. Uma função anônima literal é bem parecida com uma declaração de função normal, mas (obviamente) sem um nome. Você pode ver uma acima no corpo do laço `for`.

Funções anônimas têm várias funcionalidades que as tornam úteis, duas das quais estamos usando acima. Primeiramente, elas podem ser executadas assim que fazemos sua declaração - que é o `()` no final da função anônima. Em segundo lugar, elas mantém acesso ao escopo léxico em que são definidas - todas as variáveis que estão disponíveis no ponto em que a função anônima é declarada também estão disponíveis no corpo da função.

O corpo da função anônima acima é quase o mesmo da função no laço utilizada anteriormente. A única diferença é que cada iteração do loop vai iniciar uma nova goroutine, concorrente com o processo atual (a função `VerificadorWebsite`), e cada uma vai adicionar seu resultado ao map de resultados.

```bash
--- FAIL: TestVerificaWebsites (0.00s)
        VerificaWebsites_test.go:31: esperado map[http://google.com:true http://blog.gypsydave5.com:true waat://furhurterwe.geds:false], resultado map[]
FAIL
exit status 1
FAIL    github.com/larien/aprenda-go-com-testes/concorrencia/v2        0.010s
```

### Uma breve visita ao universo paralelo...

Você pode não ter obtido esse resultado. Você pode obter uma mensagem de pânico, que vamos falar sobre em breve. Não se preocupe se isso aparecer para você, basta você executar o teste até você *de fato* receber o resultado acima. Ou faça de conta que você recebeu. Escolha sua. Boas vindas à concorrência: quando não for trabalhada da forma correta, é difícil prever o que vai acontecer. Não se preocupe, é por isso que estamos escrevendo testes: para nos ajudar a saber quando estamos trabalhando com concorrência de forma previsível.

### ... e estamos de volta.

Acabou que os testes originais do `VerificadorWebsite` agora estão devolvendo um map vazio. O que deu de errado?

Nenhuma das goroutines que nosso loop `for` iniciou teve tempo de adicionar seu resultado ao map `resultados`; a função `VerificadorWebsite` é rápida demais para eles, e por isso retorna o map vazio.

Para consertar isso, podemos apenas esperar enquanto todas as goroutines fazem seu trabalho, para depois retornar. Dois segundos devem servir, certo?

```go
package concurrency

import "time"

type VerificadorWebsite func(string) bool

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)

    for _, url := range urls {
        go func() {
            resultados[url] = vw(url)
        }()
    }

    time.Sleep(2 * time.Second)

    return resultados
}
```

Agora, quando os testes forem executados, você vai ver (ou não - leia a mensagem no início do tópico):

```bash
--- FAIL: TestVerificaWebsites (0.00s)
        VerificaWebsites_test.go:31: esperado map[http://google.com:true http://blog.gypsydave5.com:true waat://furhurterwe.geds:false], resultado map[waat://furhurterwe.geds:false]
FAIL
exit status 1
FAIL    github.com/larien/aprenda-go-com-testes/concorrencia/v1        0.010s
```

Isso não é muito bom - por que só um resultado? Podemos arrumar isso aumentando o tempo de espera - pode tentar se preferir. Não vai funcionar. O problema aqui é que a variável `url` é reutilizada para cada iteração do laço `for` - ele recebe um valor novo de `urls` a cada vez. Mas cada uma das goroutines tem uma referência para a variável `url` - eles não têm sua própria cópia independente. Logo, *todas* estão escrevendo o valor que `url` tem no final da iteração - o último URL. E é por isso que o resultado que obtemos é a última URL.

Para consertar isso:

```go
package concorrencia

import (
    "time"
)

type VerificadorWebsite func(string) bool

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)

    for _, url := range urls {
        go func(u string) {
            resultados[u] = vw(u)
        }(url)
    }

    time.Sleep(2 * time.Second)

    return resultados
}
```

Ao passar cada função anônima como parâmetro para a URL - como `u` - e chamar a função anônima com `url` como argumento, nos certificamos de que o valor de `u` está fixado como o valor de `url` para cada iteração do laço de `url` e não pode ser modificado.

Agora, se você tiver sorte, vai obter:

```bash
PASS
ok      github.com/larien/aprenda-go-com-testes/concorrencia/v1        2.012s
```

No entanto, se não tiver sorte (isso é mais provável se estiver rodando o código com o benchmark, já que haverá mais tentativas):

```bash
fatal error: concurrent map writes

goroutine 37 [running]:
runtime.throw(0x6d74f3, 0x15)
    /usr/local/go/src/runtime/panic.go:608 +0x72 fp=0xc000034718 sp=0xc0000346e8 pc=0x42d4e2
runtime.mapassign_faststr(0x67dbe0, 0xc000082660, 0x6d33cb, 0x7, 0x0)
    /usr/local/go/src/runtime/map_faststr.go:275 +0x3bf fp=0xc000034780 sp=0xc000034718 pc=0x4139ff
github.com/larien/aprenda-go-com-testes/concorrencia/v2.VerificaWebsites.func1(0x6e6580, 0xc000082660, 0x6d33cb, 0x7)
    /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:17 +0x7f fp=0xc0000347c0 sp=0xc000034780 pc=0x64035f
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1333 +0x1 fp=0xc0000347c8 sp=0xc0000347c0 pc=0x45c661
created by github.com/larien/aprenda-go-com-testes/concorrencia/v2.VerificaWebsites
    /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:16 +0xa9

        ... e mais um monte de linhas assustadoras ...
```

Isso pode ser enorme e assustador, mas tudo o que precisamos fazer é respirar com calma e ler o stacktrace: `fatal error: concurrent map writes` (erro fatal: escrita concorrente no map). Às vezes, quando executamos nossos testes, duas das goroutines escrevem no map `resultados` ao mesmo tempo. Maps em Go não gostam quando mais de uma coisa tenta escrever algo neles ao mesmo tempo, então o `erro fatal` é gerado.

Essa é uma *condição de corrida*, um bug que aparece quando a saída do nosso software depende do timing e da sequência de eventos que não temos controle sobre. Por não termos controle exato sobre quando cada goroutine escreve no map `resultados`, ficamos vulneráveis à situação de duas goroutines escreverem nele ao mesmo tempo.

O Go nos ajuda a encontrar condições de corrida com seu [*detector de corrida*](https://blog.golang.org/race-detector) nativo. Para habilitar essa funcionalidade, execute os testes com a flag `race`: `go test -race`.

Você deve ver uma saída parecida com essa:

```bash
==================
WARNING: DATA RACE
Write at 0x00c000120089 by goroutine 6:
  reflect.typedmemmove()
      /usr/local/go/src/runtime/mbarrier.go:177 +0x0
  reflect.Value.MapIndex()
      /usr/local/go/src/reflect/value.go:1124 +0x2ae
  reflect.deepValueEqual()
      /usr/local/go/src/reflect/deepequal.go:118 +0x13be
  reflect.DeepEqual()
      /usr/local/go/src/reflect/deepequal.go:196 +0x2f0
  github.com/larien/aprenda-go-com-testes/concorrencia/v2.TestVerificaWebsites()
      /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites_test.go:30 +0x1ad
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162

Previous write at 0x00c000120089 by goroutine 8:
  github.com/larien/aprenda-go-com-testes/concorrencia/v2.VerificaWebsites.func1()
      /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:17 +0x97

Goroutine 6 (running) created at:
  testing.(*T).Run()
      /usr/local/go/src/testing/testing.go:878 +0x659
  testing.runTests.func1()
      /usr/local/go/src/testing/testing.go:1119 +0xa8
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162
  testing.runTests()
      /usr/local/go/src/testing/testing.go:1117 +0x4ee
  testing.(*M).Run()
      /usr/local/go/src/testing/testing.go:1034 +0x2ee
  main.main()
      _testmain.go:44 +0x221

Goroutine 8 (finished) created at:
  github.com/larien/aprenda-go-com-testes/concorrencia/v2.VerificaWebsites()
      /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:16 +0xb2
  github.com/larien/aprenda-go-com-testes/concorrencia/v2.TestVerificaWebsites()
      /home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites_test.go:28 +0x17f
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162
==================
```

Os detalhes ainda assim são bem difíceis de serem lidos - mas o `WARNING: DATA RACE` (CUIDADO: CONDIÇÃO DE CORRIDA) é bem claro. Lendo o corpo do erro podemos ver duas goroutines diferentes performando escritas em um map:

`Write at 0x00c000120089 by goroutine 6:`

está escrevendo no mesmo bloco de memória que:

`Previous write at 0x00c000120089 by goroutine 8:`

Além disso, conseguimos ver a linha de código onde a escrita está acontecendo:

`/home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:17 +0x97`

e a linha de código onde as goroutines 6 e 7 foram iniciadas:

`/home/larien/go/src/github.com/larien/aprenda-go-com-testes/concorrencia/v2/VerificaWebsites.go:16 +0xb2`

Tudo o que você precisa saber está impresso no seu terminal - tudo o que você tem que fazer é ser paciente o bastante para lê-lo.

### Canais

Podemos resolver essa condição de corrida coordenando nossas goroutines usando *canais*. Canais são uma estrutura de dados em Go que pode receber e enviar valores. Essas operações, junto de seus detalhes, permitem a comunicação entre processos diferentes.

Nesse caso, queremos pensar sobre a comunicação entre o processo pai e cada uma das goroutines criadas por ele de forma que façam o trabalho de executar a função `VerificadorWebsite` com a URL.

```go
package concurrency

type VerificadorWebsite func(string) bool
type resultado struct {
    string
    bool
}

func VerificaWebsites(vw VerificadorWebsite, urls []string) map[string]bool {
    resultados := make(map[string]bool)
    canalResultado := make(chan resultado)

    for _, url := range urls {
        go func(u string) {
            canalResultado <- resultado{u, vw(u)}
        }(url)
    }

    for i := 0; i < len(urls); i++ {
        resultado := <-canalResultado
        resultados[resultado.string] = resultado.bool
    }

    return resultados
}
```

Junto do map `resultados`, agora temos um `canalResultados`, que criamos da mesma forma usando `make`. O `chan resultado` é o tipo do canal - um canal de `resultado`. O tipo novo, `resultado`, foi criado para associar o retorno de `VerificadorWebsite` com a URL sendo verificada - é uma estrutura que contém uma `string` e um `bool`. Já que não precisamos que nenhum valor tenha um nome, cada um deles é anônimo dentro da struct; isso pode ser útil quando for difícil saber que nome dar a um valor.

Agora que iteramos pelas URLs, ao invés de escrever no `map` diretamente, enviamos uma struct `resultado` para cada chamada de `vw` para o `canalResultado` com uma *sintaxe de envio*. Essa sintaxe usa o operador `<-`, usando um canal à esquerda e um valor à direita:

```go
// Sintaxe de envio
canalResultado <- resultado{u, vw(u)}
```

O próximo laço `for` itera uma vez sobre cada uma das URLs. Dentro, estamos usando uma *expressão de recebimento*, que atribui um valor recebido por um canal a uma variável. Essa expressão também usa o operador `<-`, mas com os dois operandos ao posições invertidas: o canal agora fica à direita e a variável que está recebendo o valor dele fica à esquerda:

```go
// Expressão recebida
resultado := <-canalResultado
```

E depois usamos o `resultado` recebido para atualizar o map.

Ao enviar os resultados para um canal, podemos controlar o timing de cada escrita dentro do map `resultados`, garantindo que só aconteça uma por vez. Apesar de cada uma das chamadas de `vw` e cada envio ao canal resultado estar acontecendo em paralelo dentro de seu próprio processo, cada resultado está sendo resolvido de cada vez enquanto tiramos o valor do canal resultado com a expressão recebida.

Paralelizamos um pedaço do código que queríamos tornar mais rápida, enquanto mantivemos a parte que não pode acontecer em paralelo ainda acontecendo linearmente. E comunicamos diversos processos envolvidos utilizando canais.

Agora podemos executar o benchmark:

```bash
pkg: github.com/larien/aprenda-go-com-testes/concorrencia/v3
BenchmarkVerificaWebsites-8             100          23406615 ns/op
PASS
ok      github.com/larien/aprenda-go-com-testes/concorrencia/v3        2.377s
```

23406615 nanossegundos - 0.023 segundos, cerca de 100 vezes mais rápida que a função original. Um sucesso enorme.

## Resumo

Esse exercício foi um pouco mais leve na parte do TDD que o restante. Levamos um bom tempo refatorando a função `VerificaWebsites`; as entradas e saídas não mudaram, ela apenas ficou mais rápida. Mas, com os testes que já tinhamos escrito, assim como com o benchmark que escrevemos, fomos capazes de refatorar o `VerificaWebsites` de forma que mantivéssemos a confiança de que o software ainda estava funcionando, enquanto demonstramos que ela realmente havia ficado mais rápida.

Tornando as coisas mais rápidas, aprendemos sobre:

* *goroutines*, a unidade básica de concorrência em Go, que nos permite verificar mais do que um site ao mesmo tempo.
* *funções anônimas*, que usamos para iniciar cada um dos processos concorrentes que verificam os sites.
* *canais*, para nos ajudar a organizar e controlar a comunicação entre diferentes processos, nos permitindo evitar um bug de *condição de corrida*.
* *o detector de corrida*, que nos ajudou a desvendar problemas com código concorrente.

### Torne-o rápido

Uma formulação da forma ágil de desenvolver software, erroneamente atribuida a Kent Beck, é:

> [Faça funcionar, faça da forma certa, torne-o rápido](http://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast) (em inglês)

Onde 'funcionar' é fazer os testes passarem, 'forma certa' é refatorar o código e 'tornar rápido' é otimizar o código para, por exemplo, tornar sua execução rápida. Só podemos 'torná-lo rápido' quando fizermos funcionar da forma certa. Tivemos sorte que o código que estudamos já estava funcionando e não precisava ser refatorado. Nunca devemos tentar 'torná-lo rápido' antes das outras duas etapas terem sido feitas, porque:

> [Otimização prematura é a raiz de todo o mal](http://wiki.c2.com/?PrematureOptimization) -- Donald Knuth


# Select

[**Você pode encontrar todos os códigos desse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/select)

Te pediram para fazer uma função chamada `Corredor` que recebe duas URLs que "competirão" entre si através de uma chamada HTTP GET onde a primeira URL a responder será retornada. Se nenhuma delas responder dentro de 10 segundos a função deve retornar um `erro`.

Para isso, vamos utilizar:

* `net/http` para chamadas HTTP.
* `net/http/httptest` para nos ajudar a testar.
* goroutines.
* `select` para sincronizar processos.

## Escreva o teste primeiro

Vamos começar com algo simples.

```go
func TestCorredor(t *testing.T) {
    URLLenta := "http://www.facebook.com"
    URLRapida := "http://www.quii.co.uk"

    esperado := URLRapida
    resultado := Corredor(URLLenta, urlRapida)

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}
```

Sabemos que não está perfeito e que existem problemas, mas é um bom início. É importante não perder tanto tempo deixando as coisas perfeitas de primeira.

## Execute o teste

`./corredor_test.go:14:9: undefined: Corredor`

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

```go
func Corredor(a, b string) (vencedor string) {
    return
}
```

`corredor_test.go:25: resultado '', esperado 'http://www.quii.co.uk'`

## Escreva código suficiente para que o teste passe

```go
func Corredor(a, b string) (vencedor string) {
    inicioA := time.Now()
    http.Get(a)
    duracaoA := time.Since(inicioA)

    inicioB := time.Now()
    http.Get(b)
    duracaoB := time.Since(inicioB)

    if duracaoA < duracaoB {
        return a
    }

    return b
}
```

Para cada URL:

1. Usamos `time.Now()` para marcar o tempo antes de tentarmos pegar a `URL`.
2. Então usamos [`http.Get`](https://golang.org/pkg/net/http/#Client.Get) para tentar capturar os conteúdos da `URL`. Essa função retorna [`http.Response`](https://golang.org/pkg/net/http/#Response) e um `erro`, mas não temos interesse nesses valores.
3. `time.Since` pega o tempo inicial e retorna a diferença na forma de `time.Duration`.

Feito isso, podemos simplesmente comparar as durações e ver qual é mais rápida.

### Problemas

Isso pode ou não fazer com que o teste passe para você. O problema é que estamos acessando sites reais para testar nossa lógica.

Testar códigos que usam HTTP é tão comum que Go tem ferramentas na biblioteca padrão para te ajudar a testá-los.

Nos capítulos de [mock](/aprenda-go-com-testes/primeiros-passos-com-go/mocks) e [injeção de dependências](/aprenda-go-com-testes/primeiros-passos-com-go/injecao-de-dependencia), falamos sobre como idealmente não queremos depender de serviços externos para testar nosso código, pois:

* Podem ser lentos
* Podem ser inconsistentes
* Não conseguimos testar casos extremos

Na biblioteca padrão, existe um pacote chamado [`net/http/httptest`](https://golang.org/pkg/net/http/httptest/) onde é possível simular um servidor HTTP facilmente.

Vamos alterar nosso teste para usar essas simulações para termos servidores confiáveis para testar sob nosso controle.

```go
func TestCorredor(t *testing.T) {

    servidorLento := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(20 * time.Millisecond)
        w.WriteHeader(http.StatusOK)
    }))

    servidorRapido := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    }))

    URLLenta := servidorLento.URL
    URLRapida := servidorRapido.URL

    esperado := URLRapida
    resultado := Corredor(URLLenta, URLRapida)

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }

    servidorLento.Close()
    servidorRapido.Close()
}
```

A sintaxe pode parecer um pouco complicada, mas não tenha pressa.

`httptest.NewServer` recebe um `http.HandlerFunc` que vamos enviar para uma função *anônima*.

`http.HandlerFunc` é um tipo que se parece com isso: `type HandlerFunc func(ResponseWriter, *Requisicao)`.

Tudo o que assinatura diz é que ela precisa de uma função que recebe um `ResponseWriter` e uma `Requisição`, o que não é novidade para um servidor HTTP.

Acontece que não existe nenhuma mágica aqui, **também é assim que você escreveria um servidor HTTP** **real** **em Go**. A única diferença é que estamos utilizando ele dentro de um `httptest.NewServer` ,o que facilita seu uso em testes por ele encontrar uma porta aberta para escutar e você poder fechá-la quando estiverem concluídos dentro dos próprios testes.

Dentro de nossos dois servidores, fazemos com que um deles tenha um `time.Sleep` quando receber a requisição para torná-lo propositalmente mais lento que o outro. Ambos os servidores, então, devolvem uma resposta `OK` com `w.WriteHeader(http.StatusOK)` a quem realizou a chamada.

Se você rodar o teste novamente, ele definitivamente irá passar e deve ser mais rápido. Brinque com os **sleeps** para quebrar o teste propositalmente.

## Refatoração

Temos algumas duplicações tanto em nosso código de produção quanto em nosso código de teste.

```go
func Corredor(a, b string) (vencedor string) {
    duracaoA := medirTempoDeResposta(a)
    duracaoB := medirTempoDeResposta(b)

    if duracaoA < duracaoB {
        return a
    }

    return b
}

func medirTempoDeResposta(URL string) time.Duration {
    inicio := time.Now()
    http.Get(URL)
    return time.Since(inicio)
}
```

Essa "enxugada" torna nosso código `Corredor` bem mais legível.

```go
func TestCorredor(t *testing.T) {

    servidorLento := criarServidorComAtraso(20 * time.Millisecond)
    servidorRapido := criarServidorComAtraso(0 * time.Millisecond)

    defer servidorLento.Close()
    defer servidorRapido.Close()

    URLLenta := servidorLento.URL
    URLRapida := servidorRapido.URL

    esperado := URLRapida
    resultado := Corredor(URLLenta, URLRapida)

    if resultado != esperado {
        t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
    }
}

func criarServidorComAtraso(atraso time.Duration) *httptest.Server {
    return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(atraso)
        w.WriteHeader(http.StatusOK)
    }))
}
```

Fizemos a refatoração criando nossos servidores falsos numa função chamada `criarServidorComAtraso` para remover alguns códigos desnecessários do nosso teste e reduzir repetições.

### `defer`

Ao chamar uma função com o prefixo `defer`, ela será chamada *após o término da função que a contém*.

Às vezes você vai precisar liberar recursos, como fechar um arquivo ou, como no nosso caso, fechar um servidor para que esse não continue escutando a uma porta.

Utilizamos o `defer` quando queremos que a função seja executada no final de uma função, mas mantendo essa instrução próxima de onde o servidor foi criado para facilitar a vida das pessoas que forem ler o código futuramente.

Nossa refatoração é uma melhoria e uma solução razoável dados os recursos de Go que vimos até aqui, mas podemos deixar essa solução ainda mais simples.

### Sincronizando processos

* Por que estamos testando a velocidade dos sites sequencialmente quando Go é ótimo com concorrência? Devemos conseguir verificar ambos ao mesmo tempo.
* Não nos preocupamos com o *tempo exato de resposta* das requisições, apenas queremos saber qual retorna primeiro.

Para fazer isso, vamos apresentar uma nova construção chamada `select` que nos ajudará a sincronizar os processos de forma mais fácil e clara.

```go
func Corredor(a, b string) (vencedor string) {
    select {
    case <-ping(a):
        return a
    case <-ping(b):
        return b
    }
}

func ping(URL string) chan bool {
    ch := make(chan bool)
    go func() {
        http.Get(URL)
        ch <- true
    }()
    return ch
}
```

#### `ping`

Definimos a função `ping` que cria um `chan bool` e a retorna.

No nosso caso, não nos *importamos* com o tipo enviado no canal, *só queremos enviar um sinal* para dizer que terminamos, então booleanos já servem.

Dentro da mesma função, iniciamos a goroutine que enviará um sinal a esse canal uma vez que a função `http.Get(URL)` tenha sido finalizada.

#### `select`

Se você se lembrar do capítulo de [concorrência](/aprenda-go-com-testes/primeiros-passos-com-go/concorrencia), é possível esperar os valores serem enviados a um canal com `variavel := <-ch`. Isso é uma chamada *bloqueante*, pois está aguardando por um valor.

O que o `select` te permite fazer é aguardar *múltiplos* canais. O primeiro a enviar um valor "vence" e o código abaixo do `case` é executado.

Nós usamos `ping` em nosso `select` para configurar um canal para cada uma de nossas `URL`s. Qualquer um que enviar para esse canal primeiro vai ter seu código executado no `select`, que resultará nessa `URL` sendo retornada (que consequentemente será a vencedora).

Após essas mudanças, a intenção por trás de nosso código fica bem clara e sua implementação efetivamente mais simples.

### Limites de tempo

Nosso último requisito era retornar um erro se o `Corredor` demorar mais que 10 segundos.

## Escreva o teste primeiro

```go
t.Run("retorna um erro se o servidor não responder dentro de 10s", func(t *testing.T) {
    servidorA := criarServidorComAtraso(11 * time.Second)
    servidorB := criarServidorComAtraso(12 * time.Second)

    defer servidorA.Close()
    defer servidorB.Close()

    _, err := Corredor(servidorA.URL, servidorB.URL)

    if err == nil {
        t.Error("esperava um erro, mas não obtive um")
    }
})
```

Fizemos nossos servidores de teste demorarem mais que 10s para retornar para exercitar esse cenário e agora estamos esperando que `Corredor` retorne dois valores: a URL vencedora (que ignoramos nesse teste com `_`) e um `erro`.

## Execute o teste

`./corredor_test.go:37:10: assignment mismatch: 2 variables but 1 values`

## Escreva a menor quantidade de código para rodar o teste e verifique a saída do teste que falhou

```go
func Corredor(a, b string) (vencedor string, erro error) {
    select {
    case <-ping(a):
        return a, nil
    case <-ping(b):
        return b, nil
    }
}
```

Alteramos a assinatura de `Corredor` para retornar o vencedor e um `erro`. Retornamos `nil` para nossos casos de sucesso.

O compilador vai reclamar sobre seu *primeiro teste* esperar apenas um valor, então altere essa linha para `obteve, _ := Corredor(urlLenta, urlRapida)`. Sabendo disso devemos verificar se *não* obteremos um erro em nosso caso de sucesso.

Se executar isso agora, o teste irá falhar após 11 segundos.

```
--- FAIL: TestCorredor (12.00s)
    --- FAIL: TestCorredor/retorna_um_erro_se_o_teste_não_responder_dentro_de_10s (12.00s)
        corredor_test.go:40: esperava um erro, mas não obtive um.
```

## Escreva código o suficiente para fazer o teste passar

```go
func Corredor(a, b string) (vencedor string, erro error) {
    select {
    case <-ping(a):
        return a, nil
    case <-ping(b):
        return b, nil
    case <-time.After(10 * time.Second):
        return "", fmt.Errorf("tempo limite de espera excedido para %s e %s", a, b)
    }
}
```

`time.After` é uma função muito útil quando usamos `select`. Embora não ocorra em nosso caso, você pode escrever um código que bloqueia para sempre se os canais que o `select` estiver ouvindo nunca retornarem um valor. `time.After` retorna um `chan` (como `ping`) e te enviará um sinal após a quantidade de tempo definida.

Para nós isso é perfeito; se `a` ou `b` conseguir retornar teremos um vencedor, mas se chegar a 10 segundos nosso `time.After` nos enviará um sinal e retornaremos um `erro`.

### Testes lentos

O problema que temos é que esse teste demora 10 segundos para rodar. Para uma lógica tão simples, isso não parece ótimo.

O que podemos fazer é deixar esse esgotamento de tempo configurável. Então, em nosso teste, podemos ter um tempo bem curto e, quando utilizado no mundo real, esse tempo ser definido para 10 segundos.

```go
func Corredor(a, b string, tempoLimite time.Duration) (vencedor string, erro error) {
    select {
    case <-ping(a):
        return a, nil
    case <-ping(b):
        return b, nil
    case <-time.After(tempoLimite):
        return "", fmt.Errorf("tempo limite de espera excedido para %s e %s", a, b)
    }
}
```

Nosso teste não irá compilar pois não fornecemos um tempo de expiração.

Antes de nos apressar para adicionar esse valor padrão a ambos os testes, vamos *ouvi-los*.

* Nos importamos com o tempo excedido em nosso caso de teste de sucesso?
* Os requisitos foram explícitos sobre o tempo limite?

Dado esse conhecimento, vamos fazer uma pequena refatoração para ser simpático aos nossos testes e aos usuários de nosso código.

```go
var limiteDeDezSegundos = 10 * time.Second

func Corredor(a, b string) (vencedor string, erro error) {
    return Configuravel(a, b, limiteDeDezSegundos)
}

func Configuravel(a, b string, tempoLimite time.Duration) (vencedor string, erro error) {
    select {
    case <-ping(a):
        return a, nil
    case <-ping(b):
        return b, nil
    case <-time.After(tempoLimite):
        return "", fmt.Errorf("tempo limite de espera excedido para %s e %s", a, b)
    }
}
```

Nossos usuários e nosso primeiro teste podem utilizar `Corredor` (que usa `Configuravel` por baixo dos panos) e nosso caminho triste pode usar `Configuravel`.

```go
func TestCorredor(t *testing.T) {
    t.Run("compara a velocidade de servidores, retornando o endereço do mais rápido", func(t *testing.T) {
        servidorLento := criarServidorComAtraso(20 * time.Millisecond)
        servidorRapido := criarServidorComAtraso(0 * time.Millisecond)

        defer servidorLento.Close()
        defer servidorRapido.Close()

        URLLenta := servidorLento.URL
        URLRapida := servidorRapido.URL

        esperado := URLRapida
        resultado, err := Corredor(URLLenta, URLRapida)

        if err != nil {
            t.Fatalf("não esperava um erro, mas obteve um %v", err)
        }

        if resultado != esperado {
            t.Errorf("resultado '%s', esperado '%s'", resultado, esperado)
        }
    })

    t.Run("retorna um erro se o servidor não responder dentro de 10s", func(t *testing.T) {
        servidor := criarServidorComAtraso(25 * time.Millisecond)

        defer servidor.Close()

        _, err := Configuravel(servidor.URL, servidor.URL, 20*time.Millisecond)

        if err == nil {
            t.Error("esperava um erro, mas não obtive um")
        }
    })
}
```

Adicionei uma verificação final ao primeiro teste para saber se não pegamos um `erro`.

## Resumo

### `select`

* Ajuda você a escutar vários canais.
* Às vezes você pode precisar incluir `time.After` em um de seus `cases` para prevenir que seu sistema fique bloqueado para sempre.

### `httptest`

* Uma forma conveniente de criar servidores de teste para que se tenha testes confiáveis e controláveis.
* Usa as mesmas interfaces que servidores `net/http` reais, o que torna seu sistema consistente e gera menos coisas para você aprender.


# Reflexão

[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/reflection)

[Do Twitter](https://twitter.com/peterbourgon/status/1011403901419937792?s=09)

> Desafio Golang: escreva uma função `percorre(x interface{}, fn func(string))` que recebe uma struct `x` e chama `fn` para todos os campos string encontrados dentro dela. nível de dificuldade: recursão.

Para fazer isso vamos precisar usar `reflection` (reflexão).

> A reflexão em computação é a habilidade de um programa examinar sua própria estrutura, particularmente através de tipos; é uma forma de metaprogramação. Também é uma ótima fonte de confusão.

De [The Go Blog: Reflection](https://blog.golang.org/laws-of-reflection)

## O que é `interface`?

Aproveitamos a segurança de tipos que o Go nos ofereceu em termos de funções que funcionam com tipos conhecidos, como `string`, `int` e nossos próprios tipos como `ContaBancaria`.

Isso significa que de praxe temos documentação e o compilador vai reclamar se você tentar passar o tipo errado para uma função.

Só que você pode se deparar com situações em que quer escrever uma função, mas não sabe o tipo da variável em tempo de compilação.

Go nos permite contornar isso com o tipo `interface{}`, que você pode relacionar com *qualquer* tipo.

Logo, `percorre(x interface{}, fn func(string))` aceitará qualquer valor para `x`.

### Então por que não usar `interface` para tudo e ter funções bem flexíveis?

* Quando utiliza uma função que usa `interface`, você perde a segurança de tipos. E se você quisesse passar `Foo.bar` do tipo `string` para uma função, mas ao invés disso passa `Foo.baz` do tipo `int`? O compilador não vai ser capaz de informar seu erro. Você também não tem ideia *do que* pode passar para uma função. Saber que uma função recebe um `ServicoDeUsuario`, por exemplo, é muito útil.

Resumindo, só use reflexão quando realmente precisar.

Se quiser funções polimórficas, considere desenvolvê-la em torno de uma interface (não `interface{}`, só para esclarecer) para que os usuários possam usar sua função com vários tipos se implementarem os métodos que você precisar para a sua função funcionar.

Nossa função vai precisar ser capaz de trabalhar com várias coisas diferentes. Como sempre, vamos usar uma abordagem iterativa, escrevendo testes para cada coisa nova que quisermos dar suporte e refatorando ao longo do caminho até finalizarmos.

## Escreva o teste primeiro

Vamos chamar nossa função com uma estrutura que tem um campo string dentro (`x`). Depois, podemos espiar a função (`fn`) passada para ela para ver se ela foi chamada.

```go
func TestPercorre(t *testing.T) {

    esperado := "Chris"
    var resultado []string

    x := struct {
        Nome string
    }{esperado}

    percorre(x, func(entrada string) {
        resultado = append(resultado, entrada)
    })

    if len(resultado) != 1 {
        t.Errorf("número incorreto de chamadas de função: resultado %d, esperado %d", len(resultado), 1)
    }
}
```

* Queremos armazenar um slice de strings (`resultado`) que armazena quais strings foram passadas dentro de `fn` pelo `percorre`. Algumas vezes, nos capítulos anteriores, criamos tipos dedicados para isso para espionar chamadas de função/método, mas nesse caso vamos apenas passá-lo em uma função anônima para `fn` que acaba em `resultado`.
* Usamos uma `struct` anônima com um campo `Nome` do tipo string para partir para caminho "feliz" e mais simples.
* Finalmente, chamamos `percorre` com `x` e o espião e por enquanto só verificamos o tamanho de `resultado`. Teremos mais precisão nas nossas verificações quando tivermos algo bem básico funcionando.

## Tente executar o teste

```
./reflection_test.go:21:2: undefined: percorre
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Precisamos definir `percorre`.

```go
func percorre(x interface{}, fn func(entrada string)) {

}
```

Execute o teste novamente:

```
=== RUN   TestPercorre
--- FAIL: TestPercorre (0.00s)
    reflection_test.go:19: número incorreto de chamadas de função: resultado 0, esperado 1
FAIL
```

### Escreva código o suficiente para fazer o teste passar

Agora podemos chamar o espião com qualquer string para fazer o teste passar.

```go
func percorre(x interface{}, fn func(entrada string)) {
    fn("Ainda não acredito que o Brasil perdeu de 7 a 1")
}
```

Agora o teste deve estar passando. A próxima coisa que vamos precisar fazer é criar uma verificação mais específica do que está sendo chamado dentro do nosso `fn`.

## Escreva o teste primeiro

Adicione o código a seguir para o teste existente para verificar se a string passada para `fn` está correta:

```go
if resultado[0] != esperado {
    t.Errorf("resultado '%s', esperado '%s'", resultado[0], esperado)
}
```

## Execute o teste

```
=== RUN   TestPercorre
--- FAIL: TestPercorre (0.00s)
    reflection_test.go:23: resultado 'Ainda não acredito que o Brasil perdeu de 7 a 1', esperado 'Chris'
FAIL
```

### Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := reflect.ValueOf(x) // ValorDe
    campo := valor.Field(0)     // Campo
    fn(campo.String())
}
```

Esse código está *pouco seguro e muito frágil*, mas lembre-se que nosso objetivo quando estamos no "vermelho" (os testes estão falhando) é escrever a menor quantidade de código possível. Depois escrevemos mais testes para resolver nossas lacunas.

Precisamos usar o reflection para verificar as propriedades de `x`.

No [pacote reflect](https://godoc.org/reflect) existe uma função chamada `ValueOf` que retorna um `Value` (valor) de determinada variável. Isso nos permite inspecionar um valor, inclusive seus campos usados nas próximas linhas.

Então podemos presumir coisas bem otimistas sobre o valor passado:

* Podemos procurar pelo primeiro e único campo, mas pode não haver nenhum campo, o que causaria um pânico.
* Depois podemos chamar `String()` que tetorna o valor subjacente como string, mas sabemos que vai dar errado se o campo for de algum tipo que não uma string.

## Refatoração

Nosso código está passando pelo caso simples, mas sabemos que nosso código tem várias falhas.

Vamos escrever alguns testes onde passamos valores diferentes e verificaremos o array de strings com que `fn` foi chamado.

Precisamos refatorar nosso teste em um teste orientado por tabelas para tornar esse processo mais fácil para continuarmos testando novas situações.

```go
func TestPercorre(t *testing.T) {

    casos := []struct {
        Nome              string
        Entrada           interface{}
        ChamadasEsperadas []string
    }{
        {
            "Struct com um campo string",
            struct {
                Nome string
            }{"Chris"},
            []string{"Chris"},
        },
    }

    for _, teste := range casos {
        t.Run(teste.Nome, func(t *testing.T) {
            var resultado []string
            percorre(teste.Entrada, func(entrada string) {
                resultado = append(resultado, entrada)
            })

            if !reflect.DeepEqual(resultado, teste.ChamadasEsperadas) {
                t.Errorf("resultado %v, esperado %v", resultado, teste.ChamadasEsperadas)
            }
        })
    }
}
```

Agora podemos adicionar uma situação facilmente para ver o que acontece se tivermos mais de um campo string.

## Escreva o teste primeiro

Adicione o cenário a seguir nos `casos`.

```go
{
    "Struct com dois campos tipo string",
    struct {
        Nome   string
        Cidade string
    }{"Chris", "Londres"},
    []string{"Chris", "Londres"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Struct_com_dois_campos_string
    --- FAIL: TestPercorre/Struct_com_dois_campos_string (0.00s)
        reflection_test.go:40: resultado [Chris], esperado [Chris Londres]
```

## Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := reflect.ValueOf(x)

    for i := 0; i < valor.NumField(); i++ {
        campo := valor.Field(i)
        fn(campo.String())
    }
}
```

`valor` tem um método chamado `NumField` que retorna a quantidade de campos no valor. Isso nos permite iterar sobre os campos e chamar `fn`, o que faz nosso teste passar.

## Refatoração

Não parece haver nenhuma refatoração óbvia aqui que pode melhorar nosso código, então vamos continuar.

A próxima falha em `percorre` é que ela presume que todo campo é uma `string`. Vamos escrever um teste para esse caso.

## Escreva o teste primeiro

Inclua o seguinte cenário:

```go
{
    "Struct sem campo tipo string",
    struct {
        Nome  string
        Idade int
    }{"Chris", 33},
    []string{"Chris"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Struct_sem_campo_tipo_string
    --- FAIL: TestPercorre/Struct_with_noStruct_sem_campo_tipo_stringn_string_field (0.00s)
        reflection_test.go:46: resutado [Chris <int Value>], esperado [Chris]
```

## Escreva código o suficiente para fazer o teste passar

Precisamos verificar que o tipo do campo é uma `string`.

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := reflect.ValueOf(x)

    for i := 0; i < valor.NumField(); i++ {
        campo := valor.Field(i)

        if campo.Kind() == reflect.String { // Tipo
            fn(campo.String())
        }
    }
}
```

Podemos verificar seu tipo chamando a função [`Kind`](https://godoc.org/reflect#Kind).

## Refatoração

Parece que o código ainda está razoável por enquanto.

O próximo caso é: e se o valor não for uma `struct` "única"? Em outras palavras, o que acontece se tivermos uma `struct` com alguns campos aninhados?

## Escreva o teste primeiro

Estivemos usando a sintaxe de estrutura anônima para declarar tipos conforme precisávamos para nossos testes, então poderíamos continuar a fazer isso, como:

```go
{
    "Campos aninhados",
    struct {
        Nome string
        Perfil struct {
            Idade  int
            Cidade string
        }
    }{"Chris", struct {
        Idade  int
        Cidade string
    }{33, "Londres"}},
    []string{"Chris", "Londres"},
},
```

Mas podemos ver que quando você usa estruturas anônimas cada vez mais aninhadas, a sintaxe fica um pouco bagunçada. [Há uma proposta para fazer isso de forma que a sintaxe seja mais agradável](https://github.com/golang/go/issues/12854).

Vamos apenas refatorar isso criando um tipo conhecido para esse caso e referenciá-lo no nosso teste. Não é aconselhável colocar código do teste fora do teste, mas as pessoas devem ser capazes de encontrar essas estruturas procurando por sua definição.

Inclua as seguintes declarações de tipos no seu arquivo de teste:

```go
type Pessoa struct {
    Nome   string
    Perfil Perfil
}

type Perfil struct {
    Idade  int
    Cidade string
}
```

Agora podemos adicionar isso aos nossos casos ficarem bem mais legíveis que antes:

```go
{
    "Campos aninhados",
    Pessoa{
        "Chris",
        Perfil{33, "Londres"},
    },
    []string{"Chris", "Londres"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Campps_aninhados
    --- FAIL: TestPercorre/Campps_aninhados (0.00s)
        reflection_test.go:54: resultado [Chris], esperado [Chris Londres]
```

O problema é que estamos apenas iterando sobre os campos no primeiro nível da hierarquia de tipos.

## Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := reflect.ValueOf(x)

    for i := 0; i < valor.NumField(); i++ {
        campo := valor.Field(i)

        if campo.Kind() == reflect.String {
            fn(campo.String())
        }

        if campo.Kind() == reflect.Struct {
            percorre(campo.Interface(), fn)
        }
    }
}
```

A solução é bem simples. Inspecionamos seu tipo novamente e se for uma estrutura apenas chamamos `percorre` novamente na nossa estrutura de dentro.

## Refatoração

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := reflect.ValueOf(x)

    for i := 0; i < valor.NumField(); i++ {
        campo := valor.Field(i)

        switch campo.Kind() {
        case reflect.String:
            fn(campo.String())
        case reflect.Struct:
            percorre(campo.Interface(), fn)
        }
    }
}
```

Quando você está fazendo uma comparação de mesmo valor mais de uma vez, *geralmente* refatorar as condições dentro de um `switch` vai melhorar a legibilidade e tornar seu código mais fácil de estender.

E se o valor passado na estrutura for um ponteiro?

## Escreva o teste primeiro

Inclua esse caso:

```go
{
    "Ponteiros para coisas",
    &Pessoa{
        "Chris",
        Perfil{33, "Londres"},
    },
    []string{"Chris", "Londres"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Ponteiros_para_coisas
panic: reflect: call of reflect.Value.NumField on ptr Value [recovered]
    panic: reflect: call of reflect.Value.NumField on ptr Value
```

## Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := reflect.ValueOf(x)

    if valor.Kind() == reflect.Ptr {
        valor = valor.Elem()
    }

    for i := 0; i < valor.NumField(); i++ {
        campo := valor.Field(i)

        switch campo.Kind() {
        case reflect.String:
            fn(campo.String())
        case reflect.Struct:
            percorre(campo.Interface(), fn)
        }
    }
}
```

Não é possível usar o `NumField` em um ponteiro `Value` e precisamos extrair o valor antes disso usando `Elem()`.

## Refatoração

Vamos encapsular a responsabilidade de extrair o `reflect.Value` de determinada `interface{}` para uma função.

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := obtemValor(x)

    for i := 0; i < valor.NumField(); i++ {
        campo := valor.Field(i)

        switch campo.Kind() {
        case reflect.String:
            fn(campo.String())
        case reflect.Struct:
            percorre(campo.Interface(), fn)
        }
    }
}

func obtemValor(x interface{}) reflect.Value {
    valor := reflect.ValueOf(x)

    if valor.Kind() == reflect.Ptr {
        valor = valor.Elem()
    }

    return valor
}
```

Isso acaba adicionando *mais* código, mas me parece que o nível de abstração está correto.

* Obter o `reflect.Value` de `x` para que eu possa inspecioná-lo, não me importa de qual forma.
* Iterar pelos campos, fazendo o que for necessário dependendo de seu tipo.

Depois precisamos lidar com os slices.

## Escreva o teste primeiro

```go
{
    "Slices",
    []Perfil{
        {33, "Londres"},
        {34, "Reykjavík"},
    },
    []string{"Londres", "Reykjavík"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Slices
panic: reflect: call of reflect.Value.NumField on slice Value [recovered]
    panic: reflect: call of reflect.Value.NumField on slice Value
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Esse caso se parece bastante com o do ponteiro acima, pois estamos chamar `NumField` em nosso `reflect.Value`, mas não há um por não ser uma struct.

## Escreva código o suficiente para fazer o teste passar

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := obtemValor(x)

    if valor.Kind() == reflect.Slice {
        for i:=0; i< valor.Len(); i++ {
            percorre(valor.Index(i).Interface(), fn)
        }
        return
    }

    for i := 0; i < valor.NumField(); i++ {
        campo := valor.Field(i)

        switch campo.Kind() {
        case reflect.String:
            fn(campo.String())
        case reflect.Struct:
            percorre(campo.Interface(), fn)
        }
    }
}
```

## Refatoração

Isso funciona, mas está bagunçado. Não se preocupe, pois temos cada pedaço de código coberto por testes e podemos brincar da forma que quisermos.

Se formos pensar um pouco abstradamente, queremos chamar `percorre` em:

* Cada campo de uma estrutura
* Cada *coisa* de um slice

No momento nosso código faz isso, mas não reflete muito bem. Precisamos ter uma verificação no início da função para certificar se é um slice (com um `return` para parar a execução do restante do código) e se não for, só vamos presumir que é uma estrutura.

Vamos retrabalhar o código para verificar o tipo *primeiro* para depois fazermos o que importa.

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := obtemValor(x)

    switch valor.Kind() {
    case reflect.Struct:
        for i:=0; i<valor.NumField(); i++ {
            percorre(valor.Field(i).Interface(), fn)
        }
    case reflect.Slice:
        for i:=0; i<valor.Len(); i++ {
            percorre(valor.Index(i).Interface(), fn)
        }
    case reflect.String:
        fn(valor.String())
    }
}
```

Parece muito melhor! Se for uma estrutura ou um slice, iteramos sobre seus valores chamando `percorre` para cada um. Por outro lado, se for um `reflect.String`, podemos apenas chamar `fn`.

Ainda assim me parece que poderia ficar melhor. Há repetição da operação de iterar sobre campos/valores e chamar `percorre` sendo que conceitualmente são a mesma coisa.

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := obtemValor(x)

    quantidadeDeValores := 0
    var obtemCampo func(int) reflect.Value

    switch valor.Kind() {
    case reflect.String:
        fn(valor.String())
    case reflect.Struct:
        quantidadeDeValores = valor.NumField()
        obtemCampo = valor.Field
    case reflect.Slice:
        quantidadeDeValores = valor.Len()
        obtemCampo = valor.Index
    }

    for i := 0; i < quantidadeDeValores; i++ {
        percorre(obtemCampo(i).Interface(), fn)
    }
}
```

Se o `valor` for um `reflect.String`, chamamos `fn` normalmente.

Se for outra coisa, nosso `switch` vai extrair duas coisas dependendo do tipo:

* Quantos campos existem
* Como extrair o `Value` (`Field` \[campo] ou `Index` \[índice])

Uma vez que determinamos esses pontos, podemos iterar pela `quantidadeDeValores` chamando `percorre` com o resultado da função `getField`.

A partir disso, lidar com arrays deve ser simples.

## Escreva o teste primeiro

Inclua o caso:

```go
{
    "Arrays",
    [2]Perfil{
        {33, "Londres"},
        {34, "Reykjavík"},
    },
    []string{"Londres", "Reykjavík"},
}
```

## Execute o teste

```
=== RUN   TestPercorre/Arrays
    --- FAIL: TestPercorre/Arrays (0.00s)
        reflection_test.go:78: resultado [], esperado [Londres Reykjavík]
```

## Escreva código o suficiente para fazer o teste passar

Podemos resolver o caso dos arrays da mesma forma que os slices, basta adicioná-los com uma vírgula:

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := obtemValor(x)

    quantidadeDeValores := 0
    var obtemCampo func(int) reflect.Value

    switch valor.Kind() {
    case reflect.String:
        fn(valor.String())
    case reflect.Struct:
        quantidadeDeValores = valor.NumField()
        obtemCampo = valor.Field
    case reflect.Slice, reflect.Array:
        quantidadeDeValores = valor.Len()
        obtemCampo = valor.Index
    }

    for i := 0; i < quantidadeDeValores; i++ {
        percorre(obtemCampo(i).Interface(), fn)
    }
}
```

O último tipo que queremos lidar é o `map`.

## Escreva o teste primeiro

```go
{
    "Maps",
    map[string]string{
        "Foo": "Bar",
        "Baz": "Boz",
    },
    []string{"Bar", "Boz"},
},
```

## Execute o teste

```
=== RUN   TestPercorre/Maps
    --- FAIL: TestPercorre/Maps (0.00s)
        reflection_test.go:86: resultado [], esperado [Bar Boz]
```

## Escreva código o suficiente para fazer o teste passar

Novamente, se pensar um pouco de forma abstrata, percebe-se que o `map` é bem parecido com a `struct`, mas as chaves são desconhecidas em tempo de compilação.

Again if you think a little abstractly you can see that `map` is very similar to `struct`, it's just the keys are unknown at compile time.

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := obtemValor(x)

    quantidadedeValores := 0
    var obtemCampo func(int) reflect.Value

    switch valor.Kind() {
    case reflect.String:
        fn(valor.String())
    case reflect.Struct:
        quantidadeDeValores = valor.NumField()
        obtemCampo = valor.Field
    case reflect.Slice, reflect.Array:
        quantidadeDeValores = valor.Len()
        obtemCampo = valor.Index
    case reflect.Map:
        for _, chave := range valor.MapKeys() {
            percorre(valor.MapIndex(chave).Interface(), fn)
        }
    }

    for i := 0; i< quantidadeDeValores; i++ {
        percorre(obtemCampo(i).Interface(), fn)
    }
}
```

No entanto, por design, não é possível obter os valores de um map por índice. Só é possível fazer isso pela *chave*, que, caramba, acaba com a nossa abstração.

## Refatoração

Como se sente agora? Parecia que essa era uma boa abstração naquele momento, mas agora o código parece um pouco bagunçado.

*Está tudo bem!* Refatoração é uma jornada e às vezes vamos cometer erros. Um ponto importante do TDD é que ele nos dá a liberdade de testar esse tipo de coisa.

Graças aos testes implmentados a cada etapa, essa situação não é irreversível de forma alguma. Vamos apenas voltar a como estava antes da refatoração.

```go
func percorre(x interface{}, fn func(entrada string)) {
    valor := obtemValor(x)

    percorreValor := func(valor reflect.Value) {
        percorre(valor.Interface(), fn)
    }

    switch valor.Kind() {
    case reflect.String:
        fn(valor.String())
    case reflect.Struct:
        for i := 0; i < valor.NumField(); i++ {
            percorreValor(valor.Field(i))
        }
    case reflect.Slice, reflect.Array:
        for i := 0; i < valor.Len(); i++ {
            percorreValor(valor.Index(i))
        }
    case reflect.Map:
        for _, chave := range valor.MapKeys() {
            percorreValor(valor.MapIndex(chave))
        }
    }
}
```

Apresentamos o `percorreValor`, que encapsula chamadas para `percorre` dentro do nosso `switch` para que só tenham que extrair os `reflect.Value` de `valor`.

### Um último problema

Lembre que maps em Go não têm ordem garantida. Logo, às vezes os testes irão falhar porque verificamos as chamadas de `fn` em uma ordem específica.

Para arrumar isso, precisaremos mover nossa verificação com os maps para um novo teste onde não nos importamos com a ordem.

```go
t.Run("com maps", func(t *testing.T) {
    mapA := map[string]string{
        "Foo": "Bar",
        "Baz": "Boz",
    }

    var resultado []string
    percorre(mapA, func(entrada string) {
        resultado = append(resultado, entrada)
    })

    verificaSeContem(t, resultado, "Bar")
    verificaSeContem(t, resultado, "Boz")
})
```

Essa é a definição de `verificaSeContem`:

```go
func verificaSeContem(t *testing.T, palheiro []string, agulha string) {
    contem := false
    for _, x := range palheiro {
        if x == agulha {
            contem = true
        }
    }
    if !contem {
        t.Errorf("esperava-se que %+v contivesse '%s', mas não continha", palheiro, agulha)
    }
}
```

## Resumo

* Apresentamos alguns dos conceitos do pacote `reflect`.
* Usamos recursão para percorrer estruturas de dados arbitrárias.
* Houve uma reflexão quanto a uma refatoração ruim, mas não há por que se preocupar muito com isso. Isso não deve ser um problema muito grande se trabalharmos com testes de forma iterativa.
* Esse capítulo só cobre um aspecto pequeno de reflexão. [O blog do Go tem um artigo excelente cobrindo mais detalhes](https://blog.golang.org/laws-of-reflection).
* Agora que você tem conhecimento sobre reflexão, faça o possível para evitá-lo.


# Sync

[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/sync)

Queremos fazer um contador que é seguro para ser usado concorrentemente.

Vamos começar com um contador não seguro e verificar se seu comportamento funciona em um ambiente com apenas uma *thread*.

Em seguida, vamos testar sua falta de segurança com várias *goroutines* tentando usar o contador dentro dos testes e consertar essa falha.

## Escreva o teste primeiro

Queremos que nossa API nos dê um método para incrementar o contador e depois recupere esse valor.

```go
func TestContador(t *testing.T) {
    t.Run("incrementar o contador 3 vezes resulta no valor 3", func(t *testing.T) {
        contador := Contador{}
        contador.Incrementa()
        contador.Incrementa()
        contador.Incrementa()

        if contador.Valor() != 3 {
        t.Errorf("resultado %d, esperado %d", contador.Valor(), 3)
    }
    })
}
```

## Tente rodar o teste

```
./sync_test.go:9:14: undefined: Contador
```

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

Vamos definir `Contador`.

```go
type Contador struct {

}
```

Tente rodar o teste de novo e ele falhará com o seguinte erro:

```
./sync_test.go:14:10: contador.Incrementa undefined (type Contador has no field or method Incrementa)
./sync_test.go:18:13: contador.Valor undefined (type Contador has no field or method Valor)
```

Então, para finalmente fazer o teste rodar, podemos definir esses métodos:

```go
func (c *Contador) Incrementa() {

}

func (c *Contador) Valor() int {
    return 0
}
```

Agora tudo deve rodar e falhar:

```
=== RUN   TestContador
=== RUN   TestContador/incrementar_o_contador_3_vezes_resulta_no_valor_3
--- FAIL: TestContador (0.00s)
    --- FAIL: TestContador/incrementar_o_contador_3_vezes_resulta_no_valor_3 (0.00s)
        sync_test.go:27: resultado 0, esperado 3
```

## Escreva código o suficiente para fazer o teste passar

Isso deve ser simples para *experts* em Go como nós. Precisamos criar uma instância do tipo Contador e incrementá-lo com cada chamada de `Incrementa`.

```go
type Contador struct {
    valor int
}

func (c *Contador) Incrementa() {
    c.valor++
}

func (c *Contador) Valor() int {
    return c.valor
}
```

## Refatoração

Não há muito o que refatorar, mas já que iremos escrever mais testes em torno do `Contador`, vamos escrever uma pequena função de asserção `verificaContador` para que o teste fique um pouco mais legível.

```go
t.Run("incrementar o contador 3 vezes resulta no valor 3", func(t *testing.T) {
    contador := Contador{}
    contador.Incrementa()
    contador.Incrementa()
    contador.Incrementa()

    verificaContador(t, contador, 3)
})

func verificaContador(t *testing.T, resultado Contador, esperado int) {
    t.Helper()
    if resultado.Valor() != esperado {
        t.Errorf("resultado %d, esperado %d", resultado.Valor(), esperado)
    }
}
```

## Próximos passos

Isso foi muito fácil, mas agora temos um requerimento que é: o programa precisa ser seguro o suficiente para ser usado em um ambiente com acesso concorrente. Vamos precisar criar um teste para exercitar isso.

## Escreva o teste primeiro

```go
t.Run("roda concorrentemente em segurança", func(t *testing.T) {
    contagemEsperada := 1000
    contador := Contador{}

    var wg sync.WaitGroup
    wg.Add(contagemEsperada)

    for i := 0; i < contagemEsperada; i++ {
        go func(w *sync.WaitGroup) {
            contador.Incrementa()
            w.Done()
        }(&wg)
    }
    wg.Wait()

    verificaContador(t, contador, contagemEsperada)
})
```

Isso vai iterar até a nossa `contagemEsperada` e disparar uma *goroutine* para chamar `contador.Incrementa()` a cada iteração.

Estamos usando [`sync.WaitGroup`](https://golang.org/pkg/sync/#WaitGroup), que é uma maneira simples de sincronizar processos concorrentes.

> Um WaitGroup aguarda por uma coleção de *goroutines* terminar seu processamento. A *goroutine* principal faz a chamada para o `Add` definir o número de *goroutines* que serão esperadas. Então, cada uma das *goroutines* é executada e chama `Done` quando termina sua execução. Ao mesmo tempo, `Wait` pode ser usado para bloquear a execução até que todas as *goroutines* tenham terminado.

Ao esperar por `wg.Wait()` terminar sua execução antes de fazer nossas asserções, podemos ter certeza que todas as nossas *goroutines* tentaram chamar o `Incrementa` no `Contador`.

## Tente rodar o teste

```
=== RUN   TestContador/roda_concorrentemente_em_seguranca
--- FAIL: TestContador (0.00s)
    --- FAIL: TestContador/roda_concorrentemente_em_seguranca (0.00s)
        sync_test.go:26: resultado 939, esperado 1000
FAIL
```

O teste *provavelmente* vai falhar com um número diferente, mas de qualquer forma demonstra que não roda corretamente quando várias *goroutines* tentam mudar o valor do contador ao mesmo tempo.

## Escreva código o suficiente para fazer o teste passar

Uma solução simples é adicionar uma trava ao nosso `Contador`, um [`Mutex`](https://golang.org/pkg/sync/#Mutex).

> Um Mutex é uma trava de exclusão mútua. O valor zero de um Mutex é um Mutex destravado.

```go
type Contador struct {
    mu sync.Mutex
    valor int
}

func (c *Contador) Incrementa() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.valor++
}
```

Isso significa que qualquer *goroutine* chamando `Incrementa` vai receber a trava em `Contador` se for a primeira chamando essa função. Todas as outras *goroutines* vão ter que esperar por essa primeira execução até que ele esteja `Unlock`, ou destravado, antes de ganhar o acesso à instância de `Contador` alterada pela primeira chamada de função.

Agora, se você rodar o teste novamente, ele deve funcionar porque cada uma das *goroutines* tem que esperar até que seja sua vez antes de fazer alguma mudança.

## Já vi outros exemplos em que o `sync.Mutex` está embutido dentro da struct.

Você pode ver exemplos como esse:

```go
type Contador struct {
    sync.Mutex
    valor int
}
```

Há quem diga que isso torna o código um pouco mais elegante.

```go
func (c *Contador) Incrementa() {
    c.Lock()
    defer c.Unlock()
    c.valor++
}
```

Isso *parece* legal, mas, apesar de programação ser uma área altamente subjetiva, isso é **feio e errado**.

Às vezes as pessoas esquecem que tipos embutidos significam que os métodos daquele tipo se tornam *parte da interface pública*; e você geralmente não quer isso. Não se esqueçam que devemos ter muito cuidado com as nossas APIs públicas. O momento que tornamos algo público é o momento que outros códigos podem acoplar-se a ele e queremos evitar acoplamentos desnecessários.

Expôr `Lock` e `Unlock` é, no seu melhor caso, muito confuso e, no seu pior caso, potencialmente perigoso para o seu software se quem chamar o seu tipo começar a chamar esses métodos diretamente.

![Demonstração de como um usuário dessa API pode chamar erroneamente o estado da trava](https://i.imgur.com/SWYNpwm.png)

*Isso parece uma péssima ideia.*

## Copiando mutexes

Nossos testes passam, mas nosso código ainda é um pouco perigoso.

Se você rodar `go vet` no seu código, deve receber um erro similar ao seguinte:

```
sync/v2/sync_test.go:16: call of verificaContador copies lock valor: v1.Contador contains sync.Mutex
sync/v2/sync_test.go:39: verificaContador passes lock by valor: v1.Contador contains sync.Mutex
```

Uma rápida olhada na documentação do [`sync.Mutex`](https://golang.org/pkg/sync/#Mutex) nos diz o porquê:

> Um Mutex não deve ser copiado depois do primeiro uso.

Quando passamos nosso `Contador` (por valor) para `verificaContador`, ele vai tentar criar uma cópia do mutex.

Para resolver isso, devemos passar um ponteiro para o nosso `Contador`. Vamos, então, mudar a assinatura de `verificaContador`.

```go
func verificaContador(t *testing.T, resultado *Contador, esperado int)
```

Nossos testes não vão mais compilar porque estamos tentando passar um `Contador` ao invés de um `*Contador`. Para resolver isso, é melhor criar um construtor que mostra aos usuários da nossa API que seria melhor ele mesmo não inicializar seu tipo.

```go
func NovoContador() *Contador {
    return &Contador{}
}
```

Use essa função em seus testes quando for inicializar o `Contador`.

## Resumo

Falamos sobre algumas coisas do [pacote sync](https://golang.org/pkg/sync/):

* `Mutex` nos permite adicionar travas aos nossos dados
* `WaitGroup` é uma maneira de esperar as *goroutines* terminarem suas tarefas

### Quando usar travas em vez de *channels* e *goroutines*?

[Anteriormente falamos sobre *goroutines* no primeiro capítulo sobre concorrência](/aprenda-go-com-testes/primeiros-passos-com-go/concorrencia) que nos permite escrever código concorrente e seguro, então por que usar travas? [A wiki do Go tem uma página dedicada para esse tópico: Mutex ou Channel?](https://github.com/golang/go/wiki/MutexOrChannel)

> Um erro comum de um iniciante em Go é usar demais os *channels* e *goroutines* apenas porque é possível e/ou porque é divertido. Não tenha medo de usar um `sync.Mutex` se for uma solução melhor para o seu problema. Go é pragmático em deixar você escolher as ferramentas que melhor resolvem o seu problema e não te força em um único estilo de código.

Resumindo:

* **Use channels quando for passar a propriedade de um dado**
* **Use mutexes para gerenciar estados**

### go vet

Não se esqueça de usar `go vet` nos seus scripts de *build* porque ele pode te alertar a respeito de bugs mais sutis no seu código antes que eles atinjam seus pobres usuários.

### Não use códigos embutidos apenas porque é conveniente

* Pense a respeito do efeito que embutir códigos tem na sua API pública.
* Você *realmente* quer expôr esses métodos e ter pessoas acoplando o código próprio delas a ele?
* Mutexes podem se tornar um desastre de maneiras muito imprevisíveis e estranhas. Imagine um código inesperado destravando um mutex quando não deveria? Isso causaria erros muito estranhos que seriam muito difíceis de encontrar.


# Contexto

[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/primeiros-passos-com-go/contexto)

Softwares geralmente iniciam processos de longa duração e de uso intensivo de recursos (muitas vezes em goroutines). Se a ação que causou isso é cancelada ou falha por algum motivo, você precisa parar esses processos de forma consistente dentro da sua aplicação.

Se você não gerenciar isso, sua aplicação Go tão ágil da qual você tem tanto orgulho pode começar a ter problemas de desempenho difíceis de depurar.

Neste capítulo vamos usar o pacote `context` para nos ajudar a gerenciar processos de longa duração.

Vamos começar com um exemplo clássico de um servidor web que, quando iniciado, abre um processo de longa execução que vai buscar alguns dados para devolver em uma resposta.

Colocaremos em prática um cenário em que um usuário cancela a requisição antes que os dados possam ser recuperados e faremos com que o processo seja instruído a desistir.

Criei um código no caminho feliz para começarmos. Aqui está o código do nosso servidor.

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, store.Fetch())
    }
}
```

A função `Server` (servidor) recebe uma `Store` (armazenamento) e nos retorna um `http.HandlerFunc`. Store está definida como:

```go
type Store interface {
    Fetch() string
}
```

A função retornada chama o método `Fetch` (busca) da `Store` para obter os dados e escrevê-los na resposta.

Nós temos um stub correspondente para `Store` que usamos em um teste.

```go
type StubStore struct {
    response string
}

func (s *StubStore) Fetch() string {
    return s.response
}

func TestHandler(t *testing.T) {
    data := "olá, mundo"
    svr := Server(&StubStore{data})

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

    svr.ServeHTTP(response, request)

    if response.Body.String() != data {
        t.Errorf(`resultado "%s", esperado "%s"`, response.Body.String(), data)
    }
}
```

Agora que temos um caminho feliz, queremos fazer um cenário mais realista onde a `Store` não consiga finalizar o `Fetch` antes que o usuário cancele a requisição.

## Escreva o teste primeiro

Nosso handler precisará de uma maneira de dizer à `Store` para cancelar o trabalho, então atualize a interface.

```go
type Store interface {
    Fetch() string
    Cancel()
}
```

Precisaremos ajustar nosso spy para que leve algum tempo para retornar `data` e uma maneira de saber que foi dito para cancelar. Nós também o renomearemos para `SpyStore`, pois agora vamos observar a forma como ele é chamado. Ele terá que adicionar `Cancel` como um método para implementar a interface `Store`.

```go
type SpyStore struct {
    response string
    cancelled bool
}

func (s *SpyStore) Fetch() string {
    time.Sleep(100 * time.Millisecond)
    return s.response
}

func (s *SpyStore) Cancel() {
    s.cancelled = true
}
```

Vamos adicionar um novo teste onde cancelamos a requisição antes de 100 milissegundos e verificamos a store para ver se ela é cancelada.

```go
t.Run("avisa a store para cancelar o trabalho se a requisição for cancelada", 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 não foi avisada para cancelar")
      }
  })
```

Do blog da Google novamente:

> O pacote context fornece funções para derivar novos valores de contexto dos já existentes. Estes valores formam uma árvore: quando um contexto é cancelado, todos os contextos derivados dele também são cancelados.

É importante que você derive seus contextos para que os cancelamentos sejam propagados através da pilha de chamadas ([*call stack*](https://www.ardanlabs.com/blog/2015/01/stack-traces-in-go.html)) para uma determinada requisição.

O que fazemos é derivar um novo `cancellingCtx` da nossa requisição que nos retorna uma função `cancel`. Nós então programamos que a função seja chamada em 5 milissegundos usando `time.AfterFunc`. Por fim, usamos este novo contexto em nossa requisição chamando `request.WithContext`.

## Execute o teste

O teste falha como seria de esperar.

```go
--- FAIL: TestServer (0.00s)
    --- FAIL: TestServer/avisa_a_store_para_cancelar_o_trabalho_se_a_requisicao_for_cancelada (0.00s)
        context_test.go:62: store no foi avisada para cancelar
```

## Escreva código o suficiente para fazer o teste passar

Lembre-se de ser disciplinado com o TDD. Escreva a quantidade *mínima* de código para fazer nosso teste passar.

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        store.Cancel()
        fmt.Fprint(w, store.Fetch())
    }
}
```

Isto faz com que este teste passe, mas não parece tão bom! Certamente não deveríamos estar cancelando a `Store` antes do fetch em *cada requisição*.

Ao ser disciplinado ele destacou uma falha em nossos testes, isso é uma coisa boa!

Vamos precisar atualizar nosso teste de caminho feliz para verificar que ele não será cancelado.

```go
t.Run("retorna dados da store", func(t *testing.T) {
    store := SpyStore{response: data}
    svr := Server(&store)

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

    svr.ServeHTTP(response, request)

    if response.Body.String() != data {
        t.Errorf(`resultado "%s", esperado "%s"`, response.Body.String(), data)
    }

    if store.cancelled {
        t.Error("não deveria ter cancelado a store")
    }
})
```

Execute ambos os testes. O teste do caminho feliz deve agora estar falhando e somos forçados a fazer uma implementação mais sensata.

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()

        data := make(chan string, 1)

        go func() {
            data <- store.Fetch()
        }()

        select {
        case d := <-data:
            fmt.Fprint(w, d)
        case <-ctx.Done():
            store.Cancel()
        }
    }
}
```

O que fizemos aqui?

`context` tem um método `Done()` que retorna um canal que recebe um sinal quando o context estiver "done" (finalizado) ou "cancelled" (cancelado). Queremos ouvir esse sinal e chamar `store.Cancel` se o obtivermos, mas queremos ignorá-lo se a nossa `Store` conseguir finalizar o `Fetch` antes dele.

Para gerenciar isto, executamos o `Fetch` em uma goroutine e ele irá escrever o resultado em um novo channel `data`. Nós então usamos `select` para efetivamente correr para os dois processos assíncronos e então escrevemos uma resposta ou cancelamos com `Cancel`.

## Refatoração

Podemos refatorar um pouco o nosso código de teste fazendo métodos de verificação no nosso spy.

```go
func (s *SpyStore) assertWasCancelled() {
    s.t.Helper()
    if !s.cancelled {
        s.t.Errorf("store não foi avisada para cancelar")
    }
}

func (s *SpyStore) assertWasNotCancelled() {
    s.t.Helper()
    if s.cancelled {
        s.t.Errorf("store foi avisada para cancelar")
    }
}
```

Lembre-se de passar o `*testing.T` ao criar o spy.

```go
func TestServer(t *testing.T) {
    data := "olá, mundo"

    t.Run("retorna dados da store", func(t *testing.T) {
        store := &SpyStore{response: data, t: t}
        svr := Server(store)

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

        svr.ServeHTTP(response, request)

        if response.Body.String() != data {
            t.Errorf(`recebi "%s", quero "%s"`, response.Body.String(), data)
        }

        store.assertWasNotCancelled()
    })

    t.Run("avisa a store para cancelar o trabalho se a requisição for cancelada", func(t *testing.T) {
        store := &SpyStore{response: data, t: t}
        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)

        store.assertWasCancelled()
    })
}
```

Esta abordagem é boa, mas é idiomática?

Faz sentido para o nosso servidor web estar preocupado com o cancelamento manual da `Store`? E se a `Store` também depender de outros processos de execução lenta? Nós teremos que ter certeza que a `Store.Cancel` propagará corretamente o cancelamento para todos os seus dependentes.

Um dos pontos principais do `context` é que é uma maneira consistente de oferecer cancelamento.

[Da documentação do go](https://golang.org/pkg/context/)

> As requisições de entrada para um servidor devem criar um Context e as chamadas de saída para servidores devem aceitar um Context. A cadeia de chamadas de função entre eles deve propagar o Context, substituindo-o opcionalmente por um Context derivado criado usando WithCancel, WithDeadline, WithTimeout ou WithValue. Quando um Context é cancelado, todos os Contexts derivados dele também são cancelados.

Do blog da Google novamente:

> Na Google, exigimos que os programadores Go passem um parâmetro Context como o primeiro argumento para cada função no caminho de chamada entre requisições de entrada e saída. Isto permite que o código Go desenvolvido por muitas equipes diferentes interopere bem. Ele fornece um controle simples sobre timeouts e cancelamentos e garante que valores críticos, como credenciais de segurança, transitem corretamente pelos programas Go.

(Pare por um momento e pense nas ramificações de cada função tendo que enviar um context e a ergonomia disso.)

Se sentindo um pouco desconfortável? Bom. Vamos tentar seguir essa abordagem e, em vez disso, passar o `context` para nossa `Store` e deixá-la ser responsável. Dessa maneira, ela também pode passar o `context` para os seus dependentes e eles também podem ser responsáveis por se pararem.

## Escreva o teste primeiro

Teremos de alterar os nossos testes existentes, uma vez que as suas responsabilidades estão mudando. As únicas coisas que nosso handler é responsável agora é certificar-se que emite um contexto à `Store` em cascata (downstream) e que trata o erro que virá da `Store` quando é cancelada.

Vamos atualizar nossa interface `Store` para mostrar as novas responsabilidades.

```go
type Store interface {
    Fetch(ctx context.Context) (string, error)
}
```

Apague o código dentro do nosso handler por enquanto:

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
    }
}
```

Atualize nosso `SpyStore`:

```go
type SpyStore struct {
    response string
    t        *testing.T
}

func (s *SpyStore) Fetch(ctx context.Context) (string, error) {
    data := make(chan string, 1)

    go func() {
        var result string
        for _, c := range s.response {
            select {
            case <-ctx.Done():
                s.t.Log("spy store foi cancelado")
                return
            default:
                time.Sleep(10 * time.Millisecond)
                result += string(c)
            }
        }
        data <- result
    }()

    select {
    case <-ctx.Done():
        return "", ctx.Err()
    case res := <-data:
        return res, nil
    }
}
```

Temos que fazer nosso spy agir como um método real que funciona com o `context`.

Estamos simulando um processo lento onde construímos o resultado lentamente adicionando a string, caractere por caractere em uma goroutine. Quando a goroutine termina seu trabalho, ela escreve a string no channel `data`. A goroutine escuta o `ctx.Done` e irá parar o trabalho se um sinal for enviado nesse channel.

Finalmente o código usa outro `select` para esperar que a goroutine termine seu trabalho ou que o cancelamento ocorra.

É semelhante à nossa abordagem de antes onde usamos as primitivas de concorrência do Go para fazerem dois processos assíncronos disputarem um contra o outro para determinar o que retornamos.

Você usará uma abordagem similar ao escrever suas próprias funções e métodos que aceitam um `context`, por isso certifique-se de que está entendendo o que está acontecendo.

Nós removemos a referência ao `ctx` dos campos do `SpyStore` porque não é mais interessante para nós. Estamos estritamente testando o comportamento agora, que preferimos em comparação aos detalhes da implementação dos testes, como "você passou um determinado valor para a função `foo`".

Finalmente podemos atualizar nossos testes. Comente nosso teste de cancelamento para que possamos corrigir o teste do caminho feliz primeiro.

```go
t.Run("retorna dados da store", func(t *testing.T) {
    store := &SpyStore{response: data, t: t}
    svr := Server(store)

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

    svr.ServeHTTP(response, request)

    if response.Body.String() != data {
        t.Errorf(`resultado "%s", esperado "%s"`, response.Body.String(), data)
    }
})
```

## Execute o teste

```
=== RUN   TestServer/retorna_dados_da_store
--- FAIL: TestServer (0.00s)
    --- FAIL: TestServer/retorna_dados_da_store (0.00s)
        context_test.go:22: resultado "", esperado "olá, mundo"
```

## Escreva código o suficiente para fazer o teste passar

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        data, _ := store.Fetch(r.Context())
        fmt.Fprint(w, data)
    }
}
```

O nosso caminho feliz deve estar... feliz. Agora podemos corrigir o outro teste.

## Escreva o teste primeiro

Precisamos testar que não escrevemos qualquer tipo de resposta no caso de erro. Infelizmente o `httptest.ResponseRecorder` não tem uma maneira de descobrir isso, então teremos que usar nosso próprio spy para testar.

```go
type SpyResponseWriter struct {
    written bool
}

func (s *SpyResponseWriter) Header() http.Header {
    s.written = true
    return nil
}

func (s *SpyResponseWriter) Write([]byte) (int, error) {
    s.written = true
    return 0, errors.New("não implementado")
}

func (s *SpyResponseWriter) WriteHeader(statusCode int) {
    s.written = true
}
```

Nosso `SpyResponseWriter` implementa `http.ResponseWriter` para que possamos usá-lo no teste.

```go
t.Run("avisa a store para cancelar o trabalho se a requisição for cancelada", func(t *testing.T) {
    store := &SpyStore{response: data, t: t}
    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 := &SpyResponseWriter{}

    svr.ServeHTTP(response, request)

    if response.written {
        t.Error("uma resposta não deveria ter sido escrita")
    }
})
```

## Execute o teste

```
=== RUN   TestServer
=== RUN   TestServer/avisa_a_store_para_cancelar_o_trabalho_se_a_requisicao_for_cancelada
--- FAIL: TestServer (0.01s)
    --- FAIL: TestServer/avisa_a_store_para_cancelar_o_trabalho_se_a_requisicao_for_cancelada (0.01s)
        context_test.go:47: uma resposta não deveria ter sido escrita
```

## Escreva código o suficiente para fazer o teste passar

```go
func Server(store Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        data, err := store.Fetch(r.Context())

        if err != nil {
            return // todo: registre o erro como você quiser
        }

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

Podemos ver depois disso que o código do servidor se tornou simplificado, pois não é mais explicitamente responsável pelo cancelamento. Ele simplesmente passa o `context` e confia nas funções em cascata (downstream) para respeitar qualquer cancelamento que possa ocorrer.

## Resumo

### Sobre o que falamos

* Como testar um handler HTTP que teve a requisição cancelada pelo cliente.
* Como usar o contexto para gerenciar o cancelamento.
* Como escrever uma função que aceita `context` e o usa para se cancelar usando goroutines, `select` e canais.
* Seguir as diretrizes da Google a respeito de como controlar o cancelamento propagando o contexto escopado da requisição através da sua pilha de chamadas (*call stack*).
* Como levar seu próprio spy para `http.ResponseWriter` se você precisar dele.

### E quanto ao context.Value?

[Michal Štrba](https://faiface.github.io/post/context-should-go-away-go2/) e eu temos uma opinião semelhante.

> Se você usar o ctx.Value na minha empresa (inexistente), você está demitido

Alguns engenheiros têm defendido a passagem de valores através do `context` porque *parece conveniente*.

A conveniência é muitas vezes a causa do código ruim.

O problema com `context.Values` é que ele é apenas um mapa não tipado para que você não tenha nenhum tipo de segurança e você tem que lidar com ele não realmente contendo seu valor. Você tem que criar um acoplamento de chaves de mapa de um módulo para outro e se alguém muda alguma coisa começar a quebrar.

Resumindo, **se uma função necessita de alguns valores, coloque-os como parâmetros tipados em vez de tentar obtê-los a partir de `context.Value`**. Isto torna-o estaticamente verificado e documentado para que todos o vejam.

#### Mas...

Por outro lado, pode ser útil incluir informações que sejam ortogonais a uma requisição em um contexto, como um identificador único. Potencialmente esta informação não seria necessária para todas as funções da sua pilha de chamadas (*call stack*) e tornaria as suas assinaturas funcionais muito confusas.

[Jack Lindamood diz que **Context.Value deve informar, não controlar**](https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39)

> O conteúdo do context.Value é para os mantenedores e não para os usuários. Ele nunca deve ser uma entrada necessária para resultados documentados ou esperados.

### Material adicional

* Gostei muito de ler [Context should go away for Go 2 por Michal Štrba](https://faiface.github.io/post/context-should-go-away-go2/). Seu argumento é que ter que passar o `context` em toda parte é um indicador que está apontando a uma deficiência na linguagem a respeito do cancelamento. Ele diz que seria melhor se isso fosse resolvido de alguma forma no nível de linguagem, em vez de em um nível de biblioteca. Até que isso aconteça, você precisará do `context` se quiser gerenciar processos de longa duração.
* O [blog do Go descreve ainda mais a motivação para trabalhar com `context` e tem alguns exemplos](https://blog.golang.org/context)


# Introdução

Finalizada a seção dos *Primeiros passos com Go*, você já deve possuir uma base sólida sobre os principais recursos da linguagem Go e como utilizar o TDD durante o seu processo de desenvolvimento.

Nossos próximos passos vão envolver o desenvolvimento de uma aplicação. Nessa seção, todo capitulo irá depender da funcionalidade implementada pelo seu antecessor, por isso evite pulá-los.

Aqui novos conceitos serão introduzidos para facilitar a escrita de grandes aplicações e a maior parte desse projeto será realizada utilizando bibliotecas padrões da linguagem Go.

Até o final dessa seção você deve ter obtido um entendimento sólido de como escrever aplicações em Go com o apoio de testes.

* [Servidor HTTP](/aprenda-go-com-testes/criando-uma-aplicacao/servidor-http) - Vamos criar uma API que aceita requisições HTTP.
* [Respostas em JSON e roteamentos](https://github.com/larien/learn-go-with-tests/tree/037170e34714279a65f7e14fca45e6dc71b86457/criando-uma-aplicacao/json.md) - Iremos evoluir nossa API para retornar objetos JSON e vamos explorar como fazer roteamentos.
* [IO ](https://github.com/larien/learn-go-with-tests/tree/037170e34714279a65f7e14fca45e6dc71b86457/criando-uma-aplicacao/io.md) - Vamos salvar e ler dados de arquivos. Também vamos ordenar esses dados.
* [Linha de comando](https://github.com/larien/learn-go-with-tests/tree/037170e34714279a65f7e14fca45e6dc71b86457/criando-uma-aplicacao/linha-de-comando.md) - Vamos criar uma aplicação que vai ser utilizada por linha de comando no terminal, para entendermos como podemos suportar múltiplas plataformas.
* [Eventos](https://github.com/larien/learn-go-with-tests/tree/037170e34714279a65f7e14fca45e6dc71b86457/criando-uma-aplicacao/time.md) - Vamos agendar alguns eventos de processamento que irão acontecer dependendo do horário que usuário utilizou a aplicação.


# Servidor HTTP

[**Você encontra todo o código-fonte para este capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/servidor-http)

Você recebeu o desafio de criar um servidor web para que usuários possam acompanhar quantas partidas os jogadores venceram.

* `GET /jogadores/{nome}` deve retornar um número indicando o número total de vitórias
* `POST /jogadores/{nome}` deve registrar uma vitória para este nome de jogador, incrementando a cada nova chamada de submissão de dados (método HTTP `POST`).&#x20;

Vamos seguir com a abordagem do Desenvolvimento Orientado a Testes, criando software que funciona o mais rápido possível, e a cada ciclo fazendo pequenas melhorias até uma solução completa. Com essa abordagem, nós:

* Mantemos pequeno o escopo do problema em qualquer momento
* Não perdemos o foco por pensar em muito detalhes
* Se ficamos emperrados ou perdidos, podemos voltar para uma versão anterior do código sem perder muito trabalho.

## Vermelho, verde, refatore

Por todo o livro, enfatizamos o processo Desenvolvimento Orientado a Testes de escrever um teste e ver a falha (vermelho), escrever a *menor* quantidade de código para fazer o teste passar/funcionar (verde), e então fazemos a reescrita (refatoração).

A disciplina de escrever a menor quantidade de código é importante para garantir a segurança que o Desenvolvimento Orientado a Testes proporciona. Você deve se empenhar em sair do *vermelho* o quanto antes.

Kent Beck descreve essa prática como:

> Faça o teste passar rapidamente, cometendo quaisquer pecados necessários nesse processo.

E você pode cometer estes pecados porque vamos reescrever o código logo depois, com a segurança garantida pelos testes.

### E se você não fizer assim?

Quanto mais alterações você fizer enquanto seu código estiver em *vermelho*, maiores as chances de você adicionar problemas, não cobertos por testes.

A ideia é escrever iterativamente código útil em pequenos passos, guiados pelos testes, para que você não perca foco no objetivo principal.

### A galinha e o ovo

Como podemos construir isso de forma incremental? Não podemos obter um jogador (método HTTP `GET`) sem tê-lo registrado anteriormente, e parece complicado saber se a chamada ao método HTTP `POST` funcionou sem a chamada ao método HTTP `GET` já implementado.

E é nesse ponto que o uso de *classes com valores predefinidos* vai nos ajudar.

(Nota de tradução: No original, é usada a expressão *mocking*, que significa "zombar", "fazer piada" ou "enganar". Em programação, *mocking* significa criar *algo*, como uma classe ou função, que retorna os valores esperados de forma predefinida.)

* a implementação que responde ao método HTTP `GET` precisa de uma *coisa* `ArmazenamentoJogador` para obter pontuações de um nome de jogador. Isso deve ser uma interface, para que, ao executar os testes, seja possível criar um código simples de esboço para testar o código sem precisar, neste momento, implementar o código final que será usado para armazenar os dados.
* para o método HTTP `POST`, podemos *inspecionar* as chamadas feitas a `ArmazenamentoJogador` para ter certeza de que os dados são armazenados corretamente. Nossa implementação de gravação dos dados não estará vinculada à busca dos dados.
* para ver código rodando rapidamente vamos fazer uma implementação simples de armazenamento dos dados na memória, e depois podemos criar uma implementação que dá suporte ao mecanismo de armazenamento de preferência.

## Escrevendo o teste primeiro

Podemos escrever um teste e fazer funcionar retornando um valor predeterminado para nos ajudar a começar. Kent Beck se refere a isso como "Fazer de conta". Uma vez que temos um teste funcionando podemos escrever mais testes que nos ajudem a remover este valor predeterminado (constante).

Com este pequeno passo, nós começamos a ter uma estrutura inicial para o projeto funcionando corretamente, sem nos preocuparmos demais com a lógica da aplicação.

Para criar um servidor web (uma aplicação que recebe chamadas via protocolo HTTP) em Go, você vai chamar, usualmente, a função [ListenAndServe](https://golang.org/pkg/net/http/#ListenAndServe).

```go
func ListenAndServe(endereco string, tratador Handler) error
```

Isso vai iniciar um servidor web *escutando* em uma porta, criando uma gorotina para cada requisição, e repassando para um *Tratador*, que é representado pela interface [`Handler`](https://golang.org/pkg/net/http/#Handler), usada para receber a requisição e avaliar o que fazer com os dados recebidos.

```go
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
```

Esta interface define uma única função que espera dois argumentos, o primeiro que indica onde *escrevemos a resposta* e o outro com a requisição HTTP que nos foi enviada.

Vamos criar o primeiro arquivo, `servidor_test.go` e escrever um teste para a função `ServidorJogador` que recebe estes dois argumentos. A requisição enviada serve para obter a pontuação de um Nome de Jogador, que esperamos que seja `"20"`.

```go
func TestObterJogadores(t *testing.T) {
    t.Run("retornar resultado de Maria", func(t *testing.T) {
        requisicao, _ := http.NewRequest(http.MethodGet, "/jogadores/Maria", nil)
        resposta := httptest.NewRecorder()

        ServidorJogador(resposta, requisicao)

        recebido := resposta.Body.String()
        esperado := "20"

        if recebido != esperado {
            t.Errorf("recebido '%s', esperado '%s'", recebido, esperado)
        }
    })
}
```

Para testar nosso servidor, vamos precisar de uma *Requisição* (`Request`) para enviar ao servidor, e então queremos *inspecionar* o que o nosso Tratador escreve para o `ResponseWriter`.

* Nós usamos o `http.NewRequest` para criar uma requisição. O primeiro argumento é o *método* da requisição e o segundo é o caminho da requisição. O valor `nil` para o segundo argumento corresponde ao corpo (*body*) da requisição, que não precisamos definir para este teste.
* `net/http/httptest` já tem um *inspecionador* criado para nós, chamado `ResponseRecorder` (*GravadorDeResposta*), então podemos usá-lo. Este possui muitos métodos úteis para inspecionar o que foi escrito como resposta.

## Tente rodar o teste

`./servidor_test.go:13:2: undefined: ServidorJogador`

## Escreva a quantidade mínima de código para o que teste passe e verifique a falha indicada na responta do teste

O compilador está aqui para ajudar, ouça o que ele diz.

Crie o arquivo `servidor.go`, e nele a função `ServidorJogador`

```go
func ServidorJogador() {}
```

Tente novamente

```
./servidor_test.go:13:14: too many arguments in call to ServidorJogador
    have (*httptest.ResponseRecorder, *http.Request)
    want ()
```

Adicione os argumentos à função:

```go
import "net/http"

func ServidorJogador(w http.ResponseWriter, r *http.Request) {

}
```

Agora o código compila, e o teste falha.

```
--- FAIL: TestObterJogadores (0.00s)
    --- FAIL: TestObterJogadores/retornar_resultado_de_Maria (0.00s)
        servidor_test.go:20: recebido '', esperado '20'
```

## Escreva código suficiente para fazer o teste funcionar

Do capítulo sobre injeção de dependências, falamos sobre servidores HTTP com a função `Greet`. Aprendemos que a função `ResponseWriter` também implementa a interface `Writer` do pacote io, então podemos usar `fmt.Fprint` para enviar strings como respostas HTTP.

```go
func ServidorJogador(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "20")
}
```

O teste agora deve funcionar.

## Complete a estrutura

Nós queremos converter o código acima em uma aplicação. Isso é importante porque

* Teremos *software funcionando*; não queremos escrever testes apenas por escrever, e é bom ver código que funciona.
* Conforme refatoramos o código, é provável que mudaremos a estrutura do programa. Nós queremos garantir que isso é refletido em nossa aplicação também, como parte da abordagem incremental.

Crie um novo arquivo `main.go` para nossa aplicação, com o código abaixo.

```go
package main

import (
    "log"
    "net/http"
)

func main() {
    tratador := http.HandlerFunc(ServidorJogador)
    if err := http.ListenAndServe(":5000", tratador); err != nil {
        log.Fatalf("não foi possível escutar na porta 5000 %v", err)
    }
}
```

Para executar, execute o comando `go build -o programa`, que vai pegar todos os arquivos terminados em `.go` neste diretório e construir seu programa. E então você pode executar o programa rodando `./programa`.

### `http.HandlerFunc`

Anteriormente, vimos que precisamos implementar a interface `Handler` para criar um servidor. *Normalmente* fazemos isso criando um estrutura (`struct`) e fazendo com que esta implemente a interface. No entanto, mesmo que o comum seja utilizar as *estruturas* para armazenar dados, *nesse momento* não armazenamos um estado, então não parece certo criar uma *estrutura* para isso.

Usar a função [HandlerFunc](https://golang.org/pkg/net/http/#HandlerFunc) nos ajuda a resolver este problema.

> O tipo HandlerFunc é um adaptador que permite usar funções comuns como tratadores (*handlers*). Se *f* é uma função com a assinatura adequada, HandlerFunc(f) é um *Handler* que chama *f*.

```go
type HandlerFunc func(ResponseWriter, *Request)
```

Então usamos essa construção para adaptar a função `ServidorJogador`, fazendo com que esteja de acordo com a interface `Handler`.

### `http.ListenAndServe(":5000"...)`

`ListenAndServe` recebe como parâmetro um número de porta para escutar em um *tratador* (`Handler`). Se a porta já estiver sendo usada, será retornado um *erro* (`error`) para que, usando um comando `if`, possamos capturar esse erro e informar o problema para o usuário.

O que vamos fazer agora é escrever *outro* teste para nos forçar a fazer uma mudança para tentar nos afastar do valor predefinido.

## Escreva o teste primeiro

Vamos adicionar outro subteste aos nossos testes, que tenta obter a pontuação de um jogador diferente, o que causará um problema em nossa implementação que usa um código predefinido.

```go
t.Run("retornar resultado de Pedro", func(t *testing.T) {
    requisicao, _ := http.NewRequest(http.MethodGet, "/jogadores/Pedro", nil)
    resposta := httptest.NewRecorder()

    ServidorJogador(resposta, requisicao)

    recebido := resposta.Body.String()
    esperado := "10"

    if recebido != esperado {
        t.Errorf("recebido '%s', esperado '%s'", recebido, esperado)
    }
})
```

Você deve estar pensando:

> Certamente precisamos de algum tipo de armazenamento para controlar qual jogador recebe qual pontuação. É estranho que os valores sejam predefinidos em nossos testes.

Lembre-se de que estamos apenas tentando dar os menores passos possíveis; e por isso estamos, nesse momento, tentando invalidar o valor da constante.

## Tente rodar o próximo teste

```
=== RUN   TestObterJogadores
=== RUN   TestObterJogadores/retornar_resultado_de_Maria
=== RUN   TestObterJogadores/retornar_resultado_de_Pedro
    TestObterJogadores/retornar_resultado_de_Pedro: servidor_test.go:34: recebido '20', esperado '10'
--- FAIL: TestObterJogadores (0.00s)
    --- PASS: TestObterJogadores/retornar_resultado_de_Maria (0.00s)
    --- FAIL: TestObterJogadores/retornar_resultado_de_Pedro (0.00s)
```

## Escreva código suficiente para fazer passar

```go
func ServidorJogador(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    if jogador == "Maria" {
        fmt.Fprint(w, "20")
        return
    }

    if jogador == "Pedro" {
        fmt.Fprint(w, "10")
        return
    }
}
```

Este teste nos forçou a olhar para a URL da requisição e tomar uma decisão. Embora ainda estamos pensando em como armazenar os dados do jogador e as interfaces, na verdade o próximo passo a ser dado está relacionado ao *roteamento* (*routing*).

Se tivéssemos começado com o código de armazenamento dos dados, a quantidade de alterações que precisaríamos fazer seria muito grande. **Este é um pequeno passo em relação ao nosso objetivo final e foi guiado pelos testes**.

Estamos resistindo, nesse momento, à tentação de usar alguma biblioteca de roteamento, e queremos apenas dar o menor passo para fazer nossos testes funcionarem.

`r.URL.Path` retorna o caminho da requisição, e então usamos a sintaxe de slice para obter a parte final, depois de `/jogadores/`. Não é o recomendado por não ser muito robusto, mas resolve o problema por enquanto.

## Refatorar

Podemos simplificar a `ServidorJogador` separando a parte de obtenção da pontuação em uma função.

```go
func ServidorJogador(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    fmt.Fprint(w, ObterPontuacaoJogador(jogador))
}

func ObterPontuacaoJogador(nome string) string {
    if nome == "Maria" {
        return "20"
    }

    if nome == "Pedro" {
        return "10"
    }

    return ""
}
```

E podemos eliminar as repetições de parte do código dos testes montando algumas funções auxiliares("*helpers*"):

```go
func TestObterJogadores(t *testing.T) {
    t.Run("retornar resultado de Maria", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Maria")
        resposta := httptest.NewRecorder()

        ServidorJogador(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "20")
    })

    t.Run("returns Pedro's score", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Pedro")
        resposta := httptest.NewRecorder()

        ServidorJogador(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "10")
    })
}

func novaRequisicaoObterPontuacao(nome string) *http.Request {
    requisicao, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/jogadores/%s", nome), nil)
    return requisicao
}

func verificarCorpoRequisicao(t *testing.T, recebido, esperado string) {
    t.Helper()
    if recebido != esperado {
        t.Errorf("corpo da requisição é inválido, obtive '%s' esperava '%s'", recebido, esperado)
    }
}
```

Ainda assim, não estamos felizes. Não parece correto que o servidor saiba as pontuações.

Mas nossa refatoração nos mostra claramente o que fazer.

Nós movemos o cálculo de pontuação para fora do código principal que trata a requisição (*handler*) para uma função `ObterPontuacaoJogador`. Isso parece ser o lugar correto para isolar as responsabilidades usando interfaces.

Vamos alterar, em `servidor.go`, a função que refatoramos para ser uma interface:

```go
type ArmazenamentoJogador interface {
    ObterPontuacaoJogador(nome string) int
}
```

Para que o `ServidorJogador` consiga usar o `ArmazenamentoJogador`, é necessário ter uma referência a ele. Agora nos parece o momento certo para alterar nossa arquitetura, e nosso `ServidorJogador` agora se torna uma estrutura (`struct`).

```go
type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
}
```

E agora, vamos implementar a interface do *tratador* (`Handler`) adicionando um método à nossa nova estrutura `ServidorJogador` e adicionado neste método o código existente.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   jogador  := r.URL.Path[len("/jogadores/"):]
    fmt.Fprint(w, s.armazenamento.ObterPontuacaoJogador(jogador))
}
```

Outra alteração a fazer: agora usamos a `armazenamento.ObterPontuacaoJogador` para obter a pontuação, ao invés da função local definida anteriormente (e que podemos remover).

Abaixo, a listagem completa do servidor (arquivo `servidor.go`):

```go
type ArmazenamentoJogador interface {
    ObterPontuacaoJogador(nome string) int
}

type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
}

func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]
    fmt.Fprint(w, s.armazenamento.ObterPontuacaoJogador(jogador))
}
```

### Ajustar os problemas

Fizemos muitas mudanças, e sabemos que nossos testes não irão funcionar e a compilação deixou de funcionar nesse momento; mas relaxe, e deixe o compilador fazer o trabalho.

`./main.go:9:58: type ServidorJogador is not an expression`

Precisamos mudar os nossos testes, que agora devem criar uma nova instância de `ServidorJogador` e então chamar o método `ServeHTTP`.

```go
func TestObterJogadores(t *testing.T) {
    servidor := &ServidorJogador{}

    t.Run("retorna a pontuação de Maria", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Maria")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "20")
    })

    t.Run("retorna a pontuação de Pedro", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Pedro")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "10")
    })
}
```

Perceba que ainda não nos preocupamos, *por enquanto*, com o armazenamento dos dados, nós apenas queremos a compilação funcionando o quanto antes.

Você deve ter o hábito de priorizar, *sempre*, código que compila antes de ter código que passa nos testes.

Adicionando mais funcionalidades (como códigos de esboço de armazenamento) a um código que não ainda não compila, nos arriscamos a ter, potencialmente, *mais* problemas de compilação.

Agora `main.go` não vai compilar pelas mesmas razões.

```go
func main() {
    servidor := &ServidorJogador{}

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("não foi possível escutar na porta 5000 %v", err)
    }
}
```

Agora tudo compila, mas os testes falham.

```
--- FAIL: TestObterJogadores (0.00s)
    --- FAIL: TestObterJogadores/retorna_pontucao_de_Maria (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
```

Isso porque não passamos um `ArmazenamentoJogador` em nossos testes. Precisamos fazer, no arquivo `servidor_test.go` um código de esboço para nos ajudar.

```go
type EsbocoArmazenamentoJogador struct {
    pontuacoes map[string]int
}

func (e *EsbocoArmazenamentoJogador) ObterPontuacaoJogador(nome string) int {
    pontuacao := e.pontuacoes[nome]
    return pontuacao
}
```

Um *mapa* (`map`) é um jeito simples e rápido de fazer um armazenamento chave/valor para os nossos testes. Agora vamos criar um desses armazenamentos para os nossos testes e inserir `ServidorJogador`.

```go
func TestObterJogadores(t *testing.T) {
    armazenamento := EsbocoArmazenamentoJogador{
        map[string]int{
            "Maria": 20,
            "Pedro": 10,
        },
    }
    servidor := &ServidorJogador{&armazenamento}

    t.Run("retorna pontuacao de Maria", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Maria")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "20")
    })

    t.Run("retorna pontuacao de Pedro", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Pedro")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarCorpoRequisicao(t, resposta.Body.String(), "10")
    })
}
```

Nossos testes agora passam, e parecem melhores. Agora a *intenção* do nosso código é clara, por conta da adição do armazenamento. Estamos dizendo a quem lê o código que, por termos *este dado em um `ArmazenamentoJogador`*, quando você o usar com um `ServidorJogador` você deve obter as respostas definidas.

### Rodar a aplicação

Agora que nossos testes estão passando, a última coisa que precisamos fazer para completar a refatoração é verificar se a aplicação está funcionando. O programa deve iniciar, mas você vai receber uma mensagem horrível se tentar acessar o servidor em `http://localhost:5000/jogadores/Maria`.

E a razão pra isso é: não informamos um `ArmazenamentoJogador`.

Precisamos implementar um. No entanto, isso é difícil no momento, já que não estamos armazenando nenhum dado significativo e por isso vamos usar um valor predefinido, por enquanto. Vamos alterar na `main.go`:

```go
type ArmazenamentoJogadorEmMemoria struct{}

func (a *ArmazenamentoJogadorEmMemoria) ObterPontuacaoJogador(nome string) int {
    return 123
}

func main() {
    server := &ServidorJogador{&ArmazenamentoJogadorEmMemoria{}}

    if err := http.ListenAndServe(":5000", server); err != nil {
        log.Fatalf("não foi possível escutar na porta 5000 %v", err)
    }
}
```

Se você rodar novamente o `go build` e acessar a mesma URL você deve receber `"123"`. Não é fantástico, mas até armazenarmos os dados, é o melhor que podemos fazer.

Temos algumas opções para decidir o que fazer agora:

* Tratar o cenário onde o jogador não existe
* Tratar o cenário da chamado ao método HTTP `POST` em `/jogadores/{nome}`
* Não foi particularmente interessante perceber que nossa aplicação principal iniciou mas não funcionou. Tivemos que testar manualmente para ver o problema

Enquanto o cenário do tratamento ao método HTTP `POST` nos deixa mais perto do "caminho ideal", eu sinto que vai ser mais fácil atacar o cenário de "jogador não existente" antes, já que estamos neste assunto. Veremos os outros itens posteriormente.

## Escreva o teste primeiro

Adicione o cenário de um jogador inexistente aos nossos testes:

```go
t.Run("retorna 404 para jogador não encontrado", func(t *testing.T) {
    requisicao := novaRequisicaoObterPontuacao("Jorge")
    resposta := httptest.NewRecorder()

    server.ServeHTTP(resposta, requisicao)

    recebido := resposta.Code
    esperado := http.StatusNotFound

    if recebido != esperado {
        t.Errorf("recebido status %d esperado %d", recebido, esperado)
    }
})
```

## Tente rodar o teste

```
--- FAIL: TestObterJogadores (0.00s)
    --- FAIL: TestObterJogadores/retorna_404_para_jogador_não_encontrado (0.00s)
        servidor_test.go:56: recebido status 200 esperado 404
```

## Escreva código necessário para que o teste funcione

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    w.WriteHeader(http.StatusNotFound)

    fmt.Fprint(w, s.armazenamento.ObterPontuacaoJogador(jogador))
}
```

Às vezes eu me incomodo quando os defensores do Desenvolvimento Orientado a Testes dizem "tenha certeza de você escreveu apenas a mínima quantidade de código para fazer o teste funcionar", porque isso me parece muito pedante.

Mas este cenário ilustra muito bem o que querem dizer. Eu fiz o mínimo (sabendo que não era a implementação correta), que foi retornar um `StatusNotFound` em **todas as respostas**, mas todos os nossos testes estão passando!

**Implementando o mínimo para que os testes passem vai evidenciar as lacunas nos testes**. Em nosso caso, nós não estamos validando que devemos receber um `StatusOK` quando jogadores *existem* em nosso armazenamento.

Atualize os outros dois testes para validar o retorno e corrija o código.

Eis os novos testes:

```go
func TestObterJogadores(t *testing.T) {
    armazenamento := EsbocoArmazenamentoJogador{
        map[string]int{
            "Maria": 20,
            "Pedro": 10,
        },
    }
    servidor := &ServidorJogador{&armazenamento}

    t.Run("retorna pontuacao de Maria", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Maria")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarRespostaCodigoStatus(t, resposta.Code, http.StatusOK)
        verificarCorpoRequisicao(t, resposta.Body.String(), "20")
    })

    t.Run("retorna pontuacao de Pedro", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Pedro")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarRespostaCodigoStatus(t, resposta.Code, http.StatusOK)
        verificarCorpoRequisicao(t, resposta.Body.String(), "10")
    })

    t.Run("retorna 404 para jogador não encontrado", func(t *testing.T) {
        requisicao := novaRequisicaoObterPontuacao("Jorge")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        recebido := resposta.Code
        esperado := http.StatusNotFound

        if recebido != esperado {
            t.Errorf("recebido status %d esperado %d", recebido, esperado)
        }
    })
}

func novaRequisicaoObterPontuacao(nome string) *http.Request {
    req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/jogadores/%s", nome), nil)
    return req
}

func verificarCorpoRequisicao(t *testing.T, recebido, esperado string) {
    t.Helper()
    if recebido != esperado {
        t.Errorf("corpo da requisição é inválido, recebido '%s' esperado '%s'", recebido, esperado)
    }
}

func verificarRespostaCodigoStatus(t *testing.T, recebido, esperado int) {
    t.Helper()
    if recebido != esperado {
        t.Errorf("não recebeu código de status HTTP esperado, recebido %d, esperado %d", recebido, esperado)
    }
}
```

Estamos verificando o `status` (código de retorno HTTP) em todos os nossos testes, por isso existe a função auxiliar `verificarRespostaCodigoStatus` para ajudar com isso.

Agora os primeiros dois testes falham porque o código de status recebido é 404, ao invés do esperado 200. Então vamos corrigir o `ServidorJogador` para que retorne *não encontrado* (HTTP status 404) se a pontuação for 0.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    pontuacao := s.armazenamento.ObterPontuacaoJogador(jogador)

    if pontuacao == 0 {
        w.WriteHeader(http.StatusNotFound)
    }

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

### Armazenando pontuações

Agora que podemos obter pontuações de um armazenamento, também podemos armazenar novas pontuações.

## Escreva os testes primeiro

```go
func TestArmazenamentoVitorias(t *testing.T) {
    armazenamento := EsbocoArmazenamentoJogador{
        map[string]int{},
    }
    servidor := &ServidorJogador{&armazenamento}

    t.Run("retorna status 'aceito' para chamadas ao método POST", func(t *testing.T) {
        requisicao, _ := http.NewRequest(http.MethodPost, "/jogadores/Maria", nil)
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarRespostaCodigoStatus(t, resposta.Code, http.StatusAccepted)
    })
}
```

Inicialmente vamos verificar se obtemos o código de status HTTP correto ao fazer a requisição em uma rota específica usando o método POST. Isso nos permite preparar o caminho da funcionalidade que aceita um tipo diferente de requisição, e tratar de forma diferente a requisição para `GET /jogadores/{nome}`. Uma vez que isso funcione como esperado, podemos começar a testar a interação do nosso tratador (*handler*) com o armazenamento.

## Tente rodar o teste

```
--- FAIL: TestArmazenamentoVitorias (0.00s)
    --- FAIL: TestArmazenamentoVitorias/retorna_status_'aceito'_para_chamadas_ao_método_POST (0.00s)
        servidor_test.go:75: não recebeu código de status HTTP esperado, recebido 404, esperado 202
```

## Escreva código suficiente pra fazer passar

Lembre-se que estamos cometendo pecados deliberadamente, então um comando `if` para identificar o método da requisição vai resolver o problema.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {

    if r.Method == http.MethodPost {
        w.WriteHeader(http.StatusAccepted)
        return
    }

    jogador := r.URL.Path[len("/jogadores/"):]

    pontuacao := s.armazenamento.ObterPontuacaoJogador(jogador)

    if pontuacao == 0 {
        w.WriteHeader(http.StatusNotFound)
    }

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

## Refatorar

O tratador parece um pouco bagunçado agora. Vamos separar o código para ficar simples de entender e isolar as diferentes funcionalidades em novas funções.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {

    switch r.Method {
    case http.MethodPost:
        s.registrarVitoria(w)
    case http.MethodGet:
        s.mostrarPontuacao(w, r)
    }
}

func (s *ServidorJogador) mostrarPontuacao(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    pontuacao := s.armazenamento.ObterPontuacaoJogador(jogador)

    if pontuacao == 0 {
        w.WriteHeader(http.StatusNotFound)
    }

    fmt.Fprint(w, pontuacao)
}

func (s *ServidorJogador) registrarVitoria(w http.ResponseWriter) {
    w.WriteHeader(http.StatusAccepted)
}
```

Isso faz com que a responsabilidade de roteamento do `ServeHTTP` esteja mais clara; e também permite que, em nossas próximas iterações, o código para armazenamento possa estar dentro de `registrarVitoria`.

Agora, queremos verificar que, quando fazemos a chamada `POST` a `/jogadores/{nome}`, nosso `ArmazenamentoJogador` registra a vitória.

## Escreva primeiro o teste

Vamos implementar isso estendendo o `EsbocoArmazenamentoJogador` com um novo método `GravarVitoria` e então inspecionar as chamadas.

```go
type EsbocoArmazenamentoJogador struct {
    pontuacoes        map[string]int
    registrosVitorias []string
}

func (e *EsbocoArmazenamentoJogador) ObterPontuacaoJogador(nome string) int {
    pontuacao := e.pontuacoes[nome]
    return pontuacao
}

func (e *EsbocoArmazenamentoJogador) RegistrarVitoria(nome string) {
    e.registrosVitorias = append(e.registrosVitorias, nome)
}
```

Agora, para começar, estendemos o teste para verificar a quantidade de chamadas:

```go
func TestArmazenamentoVitorias(t *testing.T) {
    armazenamento := EsbocoArmazenamentoJogador{
        map[string]int{},
        nil,
    }
    servidor := &ServidorJogador{&armazenamento}

    t.Run("registra vitorias na chamada ao método HTTP POST", func(t *testing.T) {
        requisicao := novaRequisicaoRegistrarVitoriaPost("Maria")
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificarRespostaCodigoStatus(t, resposta.Code, http.StatusAccepted)

        if len(armazenamento.registrosVitorias) != 1 {
            t.Errorf("verifiquei %d chamadas a RegistrarVitoria, esperava %d", len(armazenamento.registrosVitorias), 1)
        }
    })
}

func novaRequisicaoRegistrarVitoriaPost(nome string) *http.Request {
    requisicao, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/jogadores/%s", nome), nil)
    return requisicao
}
```

## Tente rodar o teste

```
./servidor_test.go:26:17: too few values in EsbocoArmazenamentoJogador literal
./servidor_test.go:70:17: too few values in EsbocoArmazenamentoJogador literal
```

## Escreva a mínima quantidade de código para a execução do teste e verifique a falha indicada no retorno

Como adicionamos um campo, precisamos atualizar o código onde criamos o `EsbocoArmazenamentoJogador`

```go
armazenamento := EsbocoArmazenamentoJogador{
    map[string]int{},
    nil,
}
```

```
--- FAIL: TestArmazenamentoVitorias (0.00s)
    --- FAIL: TestArmazenamentoVitorias/#00 (0.00s)
        servidor_test.go:85: verifiquei 0 chamadas a RegistrarVitoria, esperava 1
```

## Escreva código suficiente para o teste passar

Como estamos apenas verificando o número de chamadas, e não seus valores específicos, nossa iteração inicial é um pouco menor.

Para conseguir invocar a `RegistrarVitoria`, precisamos atualizar a definição de `ArmazenamentoJogador` para que o `ServidorJogador` funcione como esperado.

```go
type ArmazenamentoJogador interface {
    ObterPontuacaoJogador(nome string) int
    RegistrarVitoria(nome string)
}
```

E, ao fazer isso, `main` não compila mais

```
./main.go:15:29: cannot use &ArmazenamentoJogadorEmMemoria literal (type *ArmazenamentoJogadorEmMemoria) as type ArmazenamentoJogador in field value:
        *ArmazenamentoJogadorEmMemoria does not implement ArmazenamentoJogador (missing RegistrarVitoria method)
```

O compilador nos informa o que está errado. Vamos alterar `ArmazenamentoJogadorEmMemoria`, adicionando esse método.

```go
type ArmazenamentoJogadorEmMemoria struct{}

func (s *ArmazenamentoJogadorEmMemoria) RegistrarVitoria(nome string) {}
```

Com essa alteração, o código volta a compilar - mas os testes ainda falham.

Agora que `ArmazenamentoJogador` tem o método `GravarVitoria`, podemos chamar de dentro do nosso `ServidorJogador`

```go
func (s *ServidorJogador) registrarVitoria(w http.ResponseWriter) {
    s.armazenamento.GravarVitoria("Marcela")
    w.WriteHeader(http.StatusAccepted)
}
```

Rode os testes e devem estar funcionando sem erros! Claro, `"Marcela"` não é bem o que queremos enviar para `RegistrarVitoria`, então vamos ajustar os testes.

## Escreva os testes primeiro

```go
t.Run("registra vitorias na chamada ao método HTTP POST", func(t *testing.T) {
    jogador := "Maria"

    requisicao := novaRequisicaoRegistrarVitoriaPost(jogador)
    resposta := httptest.NewRecorder()

    servidor.ServeHTTP(resposta, requisicao)

    verificarRespostaCodigoStatus(t, resposta.Code, http.StatusAccepted)

    if len(armazenamento.registrosVitorias) != 1 {
        t.Errorf("verifiquei %d chamadas a RegistrarVitoria, esperava %d", len(armazenamento.registrosVitorias), 1)
    }

    if armazenamento.registrosVitorias[0] != jogador {
        t.Errorf("não registrou o vencedor corretamente, recebi '%s', esperava '%s'", armazenamento.registrosVitorias[0], jogador)
    }
})
```

Agora sabemos que existe um elemento no slice `registrosVitorias`, e então podemos acessar, sem erros, o primeiro elemento e verificar se é igual a `jogador`.

## Tente rodar o teste

```
--- FAIL: TestArmazenamentoVitorias (0.00s)
    --- FAIL: TestArmazenamentoVitorias/registra_vitorias_na_chamada_ao_método_HTTP_POST (0.00s)
        servidor_test.go:91: não registrou o vencedor corretamente, recebi 'Marcela', esperava 'Maria'
```

## Escreva código suficiente para o teste passar

```go
func (s *ServidorJogador) registrarVitoria(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]
    s.armazenamento.RegistrarVitoria(jogador)
    w.WriteHeader(http.StatusAccepted)
}
```

Mudamos `registrarVitoria` para obter a `http.Request`, e assim conseguir extrair o nome do jogador da URL. Com o nome, podemos chamar o `armazenamento` com o valor correto para fazer os testes passarem.

## Refatorar

Podemos eliminar repetições no código, porque estamos obtendo o nome do jogador do mesmo jeito em dois lugares diferentes.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    switch r.Method {
    case http.MethodPost:
        s.registrarVitoria(w, jogador)
    case http.MethodGet:
        s.mostrarPontuacao(w, jogador)
    }
}

func (s *ServidorJogador) mostrarPontuacao(w http.ResponseWriter, jogador string) {
    pontuacao := s.armazenamento.ObterPontuacaoJogador(jogador)

    if pontuacao == 0 {
        w.WriteHeader(http.StatusNotFound)
    }

    fmt.Fprint(w, pontuacao)
}

func (s *ServidorJogador) registrarVitoria(w http.ResponseWriter, jogador string) {
    s.armazenamento.RegistrarVitoria(jogador)
    w.WriteHeader(http.StatusAccepted)
}
```

Mesmo com os testes passando, não temos código funcionando de forma ideal. Se executar a `main` e usar o programa como planejado, não vai funcionar porque ainda não nos dedicamos a implementar corretamente `ArmazenamentoJogador`. Mas isso não é um problema; como focamos no tratamento da requisição, identificamos a interface necessária, ao invés de tentar definir antecipadamente.

*Poderíamos* começar a escrever alguns testes para a `ArmazenamentoJogadorEmMemoria`, mas ela é apenas uma solução temporária até a implementação de um modo mais robusto de registrar as pontuações (por exemplo, em um banco de dados).

O que vamos fazer agora é escrever um *teste de integração* entre `ServidorJogador` e `ArmazenamentoJogadorEmMemoria` para terminar a funcionalidade. Isso vai permitir confiar que a aplicação está funcionando, sem ter que testar diretamente `ArmazenamentoJogadorEmMemoria`. E não apenas isso, mas quando implementarmos `ArmazenamentoJogador` com um banco de dados, usaremos esse mesmo teste para verificar se a implementação funciona como esperado.

### Testes de integração

Testes de integração podem ser úteis para testar partes maiores do sistema, mas saiba que:

* São mais difíceis de escrever
* Quando falham, é difícil saber o porquê (normalmente é um problema dentro de um componente do teste de integração) e pode ser difícil de corrigir
* Às vezes são mais lentos para rodar (porque são usados com componentes "reais", como um banco de dados)

Por isso, é recomendado que pesquise sobre *Pirâmide de Testes*.

## Escreva os testes primeiro

Para ser mais breve, vou te mostrar o teste de integração, já refatorado.

```go
func TestRegistrarVitoriasEBuscarEstasVitorias(t *testing.T) {
    armazenamento := NovoArmazenamentoJogadorEmMemoria()
    servidor := ServidorJogador{armazenamento}
    jogador := "Maria"

    servidor.ServeHTTP(httptest.NewRecorder(), novaRequisicaoRegistrarVitoriaPost(jogador))
    servidor.ServeHTTP(httptest.NewRecorder(), novaRequisicaoRegistrarVitoriaPost(jogador))
    servidor.ServeHTTP(httptest.NewRecorder(), novaRequisicaoRegistrarVitoriaPost(jogador))

    resposta := httptest.NewRecorder()
    servidor.ServeHTTP(resposta, novaRequisicaoObterPontuacao(jogador))
    verificarRespostaCodigoStatus(t, resposta.Code, http.StatusOK)

    verificarCorpoRequisicao(t, resposta.Body.String(), "3")
}
```

* Estamos criando os dois componentes que queremos integrar: `ArmazenamentoJogadorEmMemoria` e `ServidorJogador`.
* Então fazemos 3 requisições para registrar 3 vitórias para `jogador`. Não nos preocupamos com os códigos de retorno no teste, porque isso não é relevante para verificar se a integração funciona como esperado.
* Registramos a próxima resposta (por isso guardamos o valor em `resposta`) porque vamos obter a pontuação do `jogador`.

## Tente rodar o teste

```
--- FAIL: TestRegistrarVitoriasEBuscarEstasVitorias (0.00s)
    servidor_test.go:109: corpo da requisição é inválido, recebido '123' esperado '3'
```

## Escreva código suficiente para passar

Abaixo, há mais código do que o esperado para se escrever sem ter os testes correspondentes.

*Isso é permitido*! Ainda existem testes verificando se as coisas estão funcionando como esperado, mas não focando na parte específica em que estamos trabalhando (`ArmazenamentoJogadorEmMemoria`).

Se houvesse algum problema para continuarmos, era só reverter as alterações para antes do teste que falhou e então escrever mais testes unitários específicos para `ArmazenamentoJogadorEmMemoria`, que nos ajudariam a encontrar a solução.

```go
func NovoArmazenamentoJogadorEmMemoria() *ArmazenamentoJogadorEmMemoria {
    return &ArmazenamentoJogadorEmMemoria{map[string]int{}}
}

type ArmazenamentoJogadorEmMemoria struct {
    armazenamento map[string]int
}

func (s *ArmazenamentoJogadorEmMemoria) RegistrarVitoria(nome string) {
    ja.armazenamento[nome]++
}

func (s *ArmazenamentoJogadorEmMemoria) ObterPontuacaoJogador(nome string) int {
    return ja.armazenamento[nome]
}
```

* Para armazenar os dados, adicionamos um `map[string]int` na struct `ArmazenamentoJogadorEmMemoria`
* Para ajudar nos testes, criamos a `NewArmazenamentoJogadorEmMemoria` para inicializar o armazenamento, e o código do teste de integração foi atualizado para usar esta função (`armazenamento := NewNovoArmazenamentoJogadorEmMemoria()`).
* O resto do código é apenas para fazer o `map` funcionar.

Nosso teste de integração passa, e agora só é preciso mudar o `main` para usar o `NewNovoArmazenamentoJogadorEmMemoria()`

```go
package main

import (
    "log"
    "net/http"
)

func main() {
    servidor := &ServidorJogador{NovoArmazenamentoJogadorEmMemoria()}

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("não foi possível escutar na porta 5000 %v", err)
    }
}
```

Após compilar e rodar, use o `curl` para testar.

* Execute o comando a seguir algumas vezes, mude o nome do jogador se quiser `curl -X POST http://localhost:5000/jogadores/Maria`
* Verifique a pontuação, rodando `curl http://localhost:5000/jogadores/Maria`

Ótimo! Criamos um serviço de acordo com os padrões REST! Se quiser continuar, você pode escolher um armazenamento de dados com maior persistência, que não vai perder os dados quando o programa terminar.

* Escolher uma tecnologia de armazenamento (Bolt? Mongo? Postgres? Sistema de arquivos?)
* Fazer `PostgresArmazenamentoJogador` implementar `ArmazenamentoJogador`
* Desenvolver a funcionalidade usando Desenvolvimento Orientado a Testes para ter certeza de que funciona
* Conectar nos testes de integração, verificar se tudo funciona
* E, finalmente, integrar dentro de `main`.

## Finalizando

### `http.Handler`

* Implemente essa interface para criar servidores web
* Use `http.HandlerFunc` para transformar funções simples em `http.Handler`s
* Use `httptest.NewRecorder` para informar um `ResponseWriter` que permite inspecionar as respostas que a função tratadora envia
* Use `http.NewRequest` para construir as requisições que você espera que seu sistema receba

### Interfaces, Valores predefinidos (*Mocking*) e Injeção de Dependência

* Permitem que você construa a sua aplicação de forma iterativa, um pedaço de cada vez
* Te permite desenvolver uma funcionalidade de tratamento de requisições que precisa de um armazenamento sem precisar exatamente de uma estrutura de armazenamento
* O Desenvolvimento Orientado a Testes nos ajudou a definir as interfaces necessárias

### Cometa pecados, e daí refatore (e então registre no controle de versão)

* Você precisa tratar falhas na compilação ou nos testes como uma situação urgente, a qual precisa resolver o mais rápido possível
* Escreva apenas o código necessário para resolver o problema. *Logo depois* refatore e faça um código melhor
* Ao tentar fazer muitas alterações enquanto o código não está compilando ou os testes estão falhando, corremos o risco de acumular e agravar os problemas
* Nos manter fiéis à essa abordagem nos obriga a escrever pequenos testes, o que significa pequenas alterações, o que nos ajuda a continuar trabalhando em sistemas complexos de forma gerenciável


# JSON, roteamento e aninhamento

[**Você pode encontrar todo o código para este capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/criando-uma-aplicacao/json)

[No capítulo anterior](/aprenda-go-com-testes/criando-uma-aplicacao/servidor-http) nós criamos um servidor web para armazenar quantos jogos nossos jogadores venceram.

Nossa gerente de produtos veio com um novo requisito; criar um novo endpoint chamado `/liga` que retorne uma lista contendo todos os jogadores armazenados. Ela gostaria que isto fosse retornado como um JSON.

## Este é o código que temos até agora

```go
// servidor.go
package main

import (
    "fmt"
    "net/http"
)

type ArmazenamentoJogador interface {
    ObtemPontuacaoDoJogador(nome string) int
    GravarVitoria(nome string)
}

type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
}

func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    switch r.Method {
    case http.MethodPost:
        s.processarVitoria(w, jogador)
    case http.MethodGet:
        s.mostrarPontuacao(w, jogador)
    }
}

func (s *ServidorJogador) mostrarPontuacao(w http.ResponseWriter, jogador string) {
    pontuação := s.armazenamento.ObtemPontuacaoDoJogador(jogador)

    if pontuação == 0 {
        w.WriteHeader(http.StatusNotFound)
    }

    fmt.Fprint(w, pontuação)
}

func (s *ServidorJogador) processarVitoria(w http.ResponseWriter, jogador string) {
    s.armazenamento.GravarVitoria(jogador)
    w.WriteHeader(http.StatusAccepted)
}
```

```go
// ArmazenamentoDeJogadorNaMemoria.go
package main

func NovoArmazenamentoDeJogadorNaMemoria() *ArmazenamentoDeJogadorNaMemoria {
    return &ArmazenamentoDeJogadorNaMemoria{map[string]int{}}
}

type ArmazenamentoDeJogadorNaMemoria struct {
    armazenamento map[string]int
}

func (a *ArmazenamentoDeJogadorNaMemoria) GravarVitoria(nome string) {
    a.armazenamento[nome]++
}

func (a *ArmazenamentoDeJogadorNaMemoria) ObtemPontuacaoDoJogador(nome string) int {
    return a.armazenamento[nome]
}
```

```go
// main.go
package main

import (
    "log"
    "net/http"
)

func main() {
    servidor := &ServidorJogador{NovoArmazenamentoDeJogadorNaMemoria()}

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("não foi possível ouvir na porta 5000 %v", err)
    }
}
```

Você pode encontrar os testes correspondentes no endereço no topo do capítulo.

Nós vamos começar criando o endpoint para a tabela de `liga`.

## Escreva os testes primeiro

Ampliaremos a suite de testes existente, pois temos algumas funções de teste úteis e um `ArmazenamentoJogador` falso para usar.

```go
// server_test.go

func TestLiga(t *testing.T) {
    armazenamento := EsbocoArmazenamentoJogador{}
    servidor := &ServidorJogador{&armazenamento}

    t.Run("retorna 200 em /liga", func(t *testing.T) {
        requisicao, _ := http.NewRequest(http.MethodGet, "/liga", nil)
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificaStatus(t, resposta.Code, http.StatusOK)
    })
}
```

Antes de nos preocuparmos sobre as pontuações atuais e o JSON, nós vamos tentar manter as mudanças pequenas com o plano de ir passo a passo rumo ao nosso objetivo. O início mais simples é checar se nós conseguimos consultar `/liga` e obter um `OK` de retorno.

## Tente rodar os testes

```
=== RUN   TestLiga/retorna_200_em_/liga
panic: runtime error: slice bounds out of range [recovered]
    panic: runtime error: slice bounds out of range

goroutine 6 [running]:
testing.tRunner.func1(0xc42010c3c0)
    /usr/local/Cellar/go/1.10/libexec/src/testing/testing.go:742 +0x29d
panic(0x1274d60, 0x1438240)
    /usr/local/Cellar/go/1.10/libexec/src/runtime/panic.go:505 +0x229
github.com/larien/aprenda-go-com-testes/json-and-io/v2.(*ServidorJogador).ServeHTTP(0xc420048d30, 0x12fc1c0, 0xc420010940, 0xc420116000)
    /Users/larien/go/src/github.com/larien/aprenda-go-com-testes/json-and-io/v2/servidor.go:20 +0xec
```

Seu `ServidorJogador` deve estar sendo abortado por um panic como acima. Vá para a linha de código que está apontando para `servidor.go` no stack trace.

```go
jogador := r.URL.Path[len("/jogadores/"):]
```

No capítulo anterior, nós mencionamos que esta era uma maneira bastante ingênua de fazer o nosso roteamento. O que está acontecendo é que ele está tentando cortar a string do caminho da URL começando do índice após `/liga` e então, isto nos dá um `slice bounds out of range`.

## Escreva somente o código suficiente para fazê-lo passar

Go tem um mecanismo de rotas nativo (built-in) chamado [`ServeMux`](https://golang.org/pkg/net/http/#ServeMux) (requisição multiplexadora) que nos permite atracar um `http.Handler` para caminhos de uma requisição em específico.

Vamos cometer alguns pecados e obter os testes passando da maneira mais rápida que pudermos, sabendo que nós podemos refatorar isto com segurança uma vez que nós soubermos que os testes estão passando.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {

    roteador := http.NewServeMux()

    roteador.Handle("/liga", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    }))

    roteador.Handle("/jogadores/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        jogador := r.URL.Path[len("/jogadores/"):]

        switch r.Method {
        case http.MethodPost:
            s.processarVitoria(w, jogador)
        case http.MethodGet:
            s.mostrarPontuacao(w, jogador)
        }
    }))

    roteador.ServeHTTP(w, r)
}
```

* Quando a requisição começa nós criamos um roteador e então dizemos para o caminho `x` usar o handler `y`.
* Então para nosso novo endpoint, nós usamos `http.HandlerFunc` e uma *função anônima* para `w.WriteHeader(http.StatusOK)` quando `/liga` é requisitada para fazer nosso novo teste passar.
* Para a rota `/jogadores/` nós somente recortamos e colamos nosso código dentro de outro `http.HandlerFunc`.
* Finalmente, nós lidamos com a requisição que está vindo chamando nosso novo roteador `ServeHTTP` (notou como `ServeMux` é *também* um `http.Handler`?)

## Refatorando

`ServeHTTP` parece um pouco grande, nós podemos separar as coisas um pouco refatorando nossos handlers em métodos separados.

```go
func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(s.manipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(s.manipulaJogadores))

    roteador.ServeHTTP(w, r)
}

func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
}

func (s *ServidorJogador) manipulaJogadores(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    switch r.Method {
    case http.MethodPost:
        s.processarVitoria(w, jogador)
    case http.MethodGet:
        s.mostrarPontuacao(w, jogador)
    }
}
```

É um pouco estranho (e ineficiente) estar configurando um roteador quando uma requisição chegar e então chamá-lo. O que idealmente queremos fazer é uma função do tipo `NovoServidorJogador` que pegará nossas dependências e ao ser chamada, irá fazer a configuração única da criação do roteador. Desta forma, cada requisição pode usar somente uma instância do nosso roteador.

```go
type ServidorJogador struct {
    armazenamento  ArmazenamentoJogador
    roteador *http.ServeMux
}

func NovoServidorJogador(armazenamento ArmazenamentoJogador) *ServidorJogador {
    p := &ServidorJogador{
        armazenamento,
        http.NewServeMux(),
    }

    s.roteador.Handle("/liga", http.HandlerFunc(s.manipulaLiga))
    s.roteador.Handle("/jogadores/", http.HandlerFunc(s.manipulaJogadores))

    return s
}

func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s.roteador.ServeHTTP(w, r)
}
```

* `ServidorJogador` agora precisa armazenar um roteador.
* Nós movemos a criação do roteador para fora de `ServeHTTP` e colocamos dentro do nosso `NovoServidorJogador`, então isto só será feito uma vez, não por requisição.
* Você vai precisar atualizar todos os testes e código de produção onde nós costumávamos fazer `ServidorJogador{&armazenamento}` por `NovoServidorJogador(&armazenamento)`.

### Uma refatoração final

Tente mudar o código para o seguinte:

```go
type ServidorJogador struct {
    armazenamento  ArmazenamentoJogador
    http.Handler
}

func NovoServidorJogador(armazenamento ArmazenamentoJogador) *ServidorJogador {
    s := new(ServidorJogador)

    s.armazenamento = armazenamento

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(s.manipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(s.manipulaJogadores))

    s.Handler = roteador

    return s
}
```

Finalmente, se certifique de que você **deletou** `func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request)` por não ser mais necessária!

## Incorporando

Nós mudamos a segunda propriedade de `ServidorJogador` removendo a propriedade nomeada `roteador http.ServeMux` e substituindo por `http.Handler`; isto é chamado de *incorporar*.

> O Go não provê a noção típica de subclasses orientada por tipo, mas tem a habilidade de "emprestar" partes de uma implementação por incorporar tipos dentro de uma struct ou interface.

[Effective Go - Embedding](https://golang.org/doc/effective_go.html#embedding)

O que isto quer dizer é que nosso `ServidorJogador` agora tem todos os métodos que `http.Handler` têm, que é somente o `ServeHTTP`.

Para "preencher" o `http.Handler` nós atribuímos ele para o `roteador` que nós criamos em `NovoServidorJogador`. Nós podemos fazer isso porque `http.ServeMux` tem o método `ServeHTTP`.

Isto nos permite remover nosso próprio método `ServeHTTP`, pois nós já estamos expondo um via o tipo incorporado.

Incorporamento é um recurso muito interessante da linguagem. Você pode usar isto com interfaces para compor novas interfaces.

```go
type Animal interface {
    Comedor
    Dormente
}
```

E você pode usar isto com tipos concretos também, não somente interfaces. Como você pode esperar, se você incorporar um tipo concreto você vai ter acesso a todos os seus métodos e campos públicos.

### Alguma desvantagem?

Você deve ter cuidado ao incorporar tipos porque você vai expor todos os métodos e campos públicos do tipo que você incorporou. Em nosso caso, está tudo bem porque nós haviamos incorporado apenas a *interface* que nós queremos expôr (`http.Handler`).

Se nós tivéssemos sido "preguiçosos" e incorporado `http.ServeMux` (o tipo concreto) por exemplo, também funcionaria *porém* os usuários de `ServidorJogador` seriam capazes de adicionar novas rotas ao nosso servidor porque o método `Handle(path, handler)` seria público.

**Quando incorporamos tipos, realmente devemos pensar sobre qual o impacto que isto terá em nossa API pública**

Isto é um erro *muito* comum de mau uso de incorporamento, que termina poluindo nossas APIs e expondo os métodos internos dos seus tipos incorporados.

Agora que nós reestruturamos nossa aplicação, nós podemos facilmente adicionar novas rotas e botar para funcionar nosso endpoint `/liga`. Agora precisamos fazê-lo retornar algumas informações úteis.

Nós devemos retornar um JSON semelhante a este:

```javascript
[
   {
      "Nome":"Bill",
      "Vitórias":10
   },
   {
      "Nome":"Alice",
      "Vitórias":15
   }
]
```

## Escreva o teste primeiro

Nós vamos começar tentando analizar a resposta dentro de algo mais significativo.

```go
func TestLiga(t *testing.T) {
    armazenamento := EsbocoArmazenamentoJogador{}
    servidor := NovoServidorJogador(&armazenamento)

    t.Run("retorna 200 em /liga", func(t *testing.T) {
        requisicao, _ := http.NewRequest(http.MethodGet, "/liga", nil)
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        var obtido []Jogador

        err := json.NewDecoder(resposta.Body).Decode(&obtido)

        if err != nil {
            t.Fatalf ("Não foi possível fazer parse da resposta do servidor '%s' no slice de Jogador, '%v'", resposta.Body, err)
        }

        verificaStatus(t, resposta.Code, http.StatusOK)
    })
}
```

### Por que não testar o JSON como texto puro?

Você pode argumentar que um simples teste inicial poderia só comparar que o não foi possível ouvir na porta 5000 tem um particular texto em JSON.

Na minha experiência, testes que comparam JSONs de forma literal possuem os seguintes problemas:

* *Fragilidade*. Se você mudar o modelo dos dados seu teste irá falhar.
* *Difícil de debugar*. Pode ser complicado de entender qual é o problema real ao se comparar dois textos JSON.
* *Má intenção*. Embora a saída deva ser JSON, o que é realmente importante é exatamente o que o dado é, ao invés de como ele está codificado.
* *Re-testando a biblioteca padrão*. Não há a necessidade de testar como a biblioteca padrão gera JSON, ela já está testada. Não teste o código de outras pessoas.

Ao invés disso, nós poderíamos analisar o JSON dentro de estruturas de dados que são relevantes para nós e nossos testes.

### Modelagem de dados

Dado o modelo de dados do JSON, parece que nós precisamos de uma lista de `Jogador` com alguns campos, sendo assim nós criaremos um novo tipo para capturarmos isso.

```go
type Jogador struct {
    Nome string
    Vitorias int
}
```

### Decodificação de JSON

```go
var obtido []Jogador
err := json.NewDecoder(resposta.Body).Decode(&obtido)
```

Para analizar o JSON dentro de nosso modelo de dados nós criamos um `Decoder` do pacote `encoding/json` e então chamamos seu método `Decode`. Para criar um `Decoder` é necessário ler de um `io.Reader`, que em nosso caso é nossa própria resposta `Body`.

`Decode` pega o endereço da coisa que nós estamos tentando decodificar, e é por isso que nós declaramos um slice vazio de `Jogador` na linha anterior.

Esse processo de analisar um JSON pode falhar, então `Decode` pode retornar um `error`. Não há ponto de continuidade para o teste se isto acontecer, então nós checamos o erro e paramos o teste com `t.Fatalf`. Note que nós exibimos o não foi possível ouvir na porta 5000 junto do erro, pois é importante para qualquer outra pessoa que esteja rodando os testes ver que o texto não pôde ser analisado.

## Tente rodar o teste

```
=== RUN   TestLiga/retorna_200_em_/liga
    --- FAIL: TestLiga/retorna_200_em_/liga (0.00s)
        server_test.go:107: Não foi possível fazer parse da resposta do servidor '' no slice de Jogador, 'unexpected end of JSON input'
```

Nosso endpoint atualmente não retorna um corpo, então isso não pode ser analisado como JSON.

## Escreva código suficiente para fazê-lo passar

```go
func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    tabelaDaLiga := []Jogador{
        {"Chris", 20},
    }

    json.NewEncoder(w).Encode(tabelaDaLiga)

    w.WriteHeader(http.StatusOK)
}
```

Os testes agora passam.

### Codificando e decodificando

Note a amável simetria na biblioteca padrão.

* Para criar um `Encoder` você precisa de um `io.Writer` que é o que `http.ResponseWriter` implementa.
* Para criar um `Decoder` você precisa de um `io.Reader` que o campo `Body` da nossa resposta implementa.

Ao longo deste livro, nós temos usado `io.Writer`. Isso é uma outra demonstração desta prevalência nas bibliotecas padrões e de como várias bibliotecas facilmente trabalham em conjunto com elas.

## Refatoração

Seria legal introduzir uma separação de conceitos entre nosso handler e o trecho de obter o `tabelaDaLiga`. Como sabemos, nós não vamos codificar isso por agora.

```go
func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(s.obterTabelaDaLiga())
    w.WriteHeader(http.StatusOK)
}

func (s *ServidorJogador) obterTabelaDaLiga() []Jogador{
    return []Jogador{
        {"Chris", 20},
    }
}
```

Mais adiante, nós vamos querer estender nossos testes para então podermos controlar exatamente qual dado nós queremos receber de volta.

## Escreva o teste primeiro

Nós podemos atualizar o teste para afirmar que a tabela das ligas contem alguns jogadores que nós vamos pôr em nossa loja.

Atualize `EsbocoArmazenamentoJogador` para permitir que ele armazene uma liga, que é apenas um slice de `Jogador`. Nós vamos armazenar nossos dados esperados lá.

```go
type EsbocoArmazenamentoJogador struct {
    pontuações   map[string]int
    chamadasDeVitoria []string
    liga []Jogador
}
```

Adiante, atualize nossos testes colocando alguns jogadores na propriedade da liga, para então afirmar que eles foram retornados do nosso servidor.

```go
func TestLiga(t *testing.T) {

    t.Run("retorna a tabela da Liga como JSON", func(t *testing.T) {
        ligaEsperada := []Jogador{
            {"Cleo", 32},
            {"Chris", 20},
            {"Tiest", 14},
        }

        armazenamento := EsbocoArmazenamentoJogador{nil, nil, ligaEsperada}
        servidor := NovoServidorJogador(&armazenamento)

        requisicao, _ := http.NewRequest(http.MethodGet, "/liga", nil)
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        var obtido []Jogador

        err := json.NewDecoder(resposta.Body).Decode(&obtido)

        if err != nil {
            t.Fatalf("Não foi possível fazer parse da resposta do servidor '%s' no slice de Jogador, '%v'", resposta.Body, err)
        }

        verificaStatus(t, resposta.Code, http.StatusOK)

        if !reflect.DeepEqual(obtido, ligaEsperada) {
            t.Errorf("obtido %v esperado %v", obtido, ligaEsperada)
        }
    })
}
```

## Tente rodar o teste

```
./server_test.go:33:3: too few values in struct initializer
./server_test.go:70:3: too few values in struct initializer
```

## Escreva o minimo de código para que o teste rode e cheque as falhas na saída dele.

Você vai precisar atualizar os outros testes, assim como nós temos um novo campo em `EsbocoArmazenamentoJogador`; ponha-o como nulo para os outros testes.

Tente executar os testes novamente e você deverá ter:

```
=== RUN   TestLiga/retorna_a_tabela_da_liga_como_JSON
    --- FAIL: TestLiga/retorna_a_tabela_da_liga_como_JSON (0.00s)
        server_test.go:124: obtido [{Chris 20}] esperado [{Cleo 32} {Chris 20} {Tiest 14}]
```

## Escreva código suficiente para fazê-lo passar

Nós sabemos que o dado está em nosso `EsbocoArmazenamentoJogador` e nós abstraímos esses dados para uma interface `ArmazenamentoJogador`. Nós precisamos atualizar isto então qualquer um passando-nos um `ArmazenamentoJogador` pode prover-nos com dados para as ligas.

```go
type ArmazenamentoJogador interface {
    ObtemPontuacaoDoJogador(nome string) int
    GravarVitoria(nome string)
    ObterLiga() []Jogador
}
```

Agora nós podemos atualizar o código do nosso handler para chamar isto ao invés de retornar uma lista manualmente escrita. Delete nosso método `obterTabelaDaLiga()` e então atualize `manipulaLiga` para chamar `ObterLiga()`.

```go
func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(s.armazenamento.ObterLiga())
    w.WriteHeader(http.StatusOK)
}
```

Tente executar os testes:

```
# github.com/larien/aprenda-go-com-testes/json-and-io/v4
./main.go:9:50: cannot use NovoArmazenamentoDeJogadorNaMemoria() (type *ArmazenamentoDeJogadorNaMemoria) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *ArmazenamentoDeJogadorNaMemoria does not implement ArmazenamentoJogador (missing ObterLiga method)
./servidor_integration_test.go:11:27: cannot use armazenamento (type *ArmazenamentoDeJogadorNaMemoria) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *ArmazenamentoDeJogadorNaMemoria does not implement ArmazenamentoJogador (missing ObterLiga method)
./server_test.go:36:28: cannot use &armazenamento (type *EsbocoArmazenamentoJogador) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *EsbocoArmazenamentoJogador does not implement ArmazenamentoJogador (missing ObterLiga method)
./server_test.go:74:28: cannot use &armazenamento (type *EsbocoArmazenamentoJogador) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *EsbocoArmazenamentoJogador does not implement ArmazenamentoJogador (missing ObterLiga method)
./server_test.go:106:29: cannot use &armazenamento (type *EsbocoArmazenamentoJogador) as type ArmazenamentoJogador in argument to NovoServidorJogador:
    *EsbocoArmazenamentoJogador does not implement ArmazenamentoJogador (missing ObterLiga method)
```

O compilador está reclamando porque `ArmazenamentoDeJogadorNaMemoria` e `EsbocoArmazenamentoJogador` não tem os novos métodos que nós adicionamos em nossa interface.

Para `EsbocoArmazenamentoJogador` isto é bem fácil, apenas retorne o campo `liga` que nós adicionamos anteriormente.

```go
func (s *EsbocoArmazenamentoJogador) ObterLiga() []Jogador {
    return s.liga
}
```

Aqui está uma lembrança de como `InMemoryStore` é implementado:

```go
type ArmazenamentoDeJogadorNaMemoria struct {
    armazenamento map[string]int
}
```

Embora seja bastante simples para implementar `ObterLiga` "propriamente", iterando sobre o map, lembre que nós estamos apenas tentando *escrever o mínimo de código para fazer os testes passarem*.

Então vamos apenas deixar o compilador feliz por enquanto e viver com o desconfortável sentimento de uma implementação incompleta em nosso `InMemoryStore`.

```go
func (a *ArmazenamentoDeJogadorNaMemoria) ObterLiga() []Jogador {
    return nil
}
```

O que isto está realmente nos dizendo é que *depois* nós vamos querer testar isto, porém vamos estacionar isto por hora.

Tente executar os testes, o compilador deve passar e os testes deverão estar passando!

## Refatoração

O código de teste não transmite suas intenções muito bem e possui vários trechos que podem ser refatorados.

```go
t.Run("retorna a tabela da Liga como JSON", func(t *testing.T) {
    ligaEsperada := []Jogador{
        {"Cleo", 32},
        {"Chris", 20},
        {"Tiest", 14},
    }

    armazenamento := EsbocoArmazenamentoJogador{nil, nil, ligaEsperada}
    servidor := NovoServidorJogador(&armazenamento)

    requisicao := novaRequisicaoDeLiga()
    resposta := httptest.NewRecorder()

    servidor.ServeHTTP(resposta, requisicao)

    obtido := obterLigaDaResposta(t, resposta.Body)
    verificaStatus(t, resposta.Code, http.StatusOK)
    verificaLiga(t, obtido, ligaEsperada)
})
```

Aqui estão os novos helpers:

```go
func obterLigaDaResposta(t *testing.T, body io.Reader) (liga []Jogador) {
    t.Helper()
    err := json.NewDecoder(body).Decode(&liga)

    if err != nil {
        t.Fatalf("Não foi possível fazer parse da resposta do servidor '%s' no slice de Jogador, '%v'", body, err)
    }

    return
}

func verificaLiga(t *testing.T, obtido, esperado []Jogador) {
    t.Helper()
    if !reflect.DeepEqual(obtido, esperado) {
        t.Errorf("obtido %v esperado %v", obtido, esperado)
    }
}

func novaRequisicaoDeLiga() *http.Request {
    req, _ := http.NewRequest(http.MethodGet, "/liga", nil)
    return req
}
```

Uma última coisa que nós precisamos fazer para nosso servidor funcionar é ter certeza de que nós retornamos um `content-type` correto na resposta, então as máquinas podem reconhecer que nós estamos retornando um `JSON`.

## Escreva os testes primeiro

Adicione essa afirmação no teste existente

```go
if resposta.Result().Header.Get("content-type") != "application/json" {
    t.Errorf("resposta não tinha o tipo de conteúdo de application/json, obtido %v", resposta.Result().Header)
}
```

## Tente rodar o teste

```
=== RUN   TestLiga/retorna_a_tabela_da_liga_como_JSON
    --- FAIL: TestLiga/retorna_a_tabela_da_liga_como_JSON (0.00s)
        server_test.go:124: resposta não tinha o tipo de conteúdo de application/json, obtido map[Content-Type:[text/plain; charset=utf-8]]
```

## Escreva código suficiente para fazê-lo passar

Atualize `manipulaLiga`

```go
func (s *ServidorJogador) manipulaLiga(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("content-type", "application/json")
    json.NewEncoder(w).Encode(s.armazenamento.ObterLiga())
}
```

O teste deve passar.

## Refatoração

Adicione um helper para `verificaTipoDoConteudo`.

```go
const tipoDoConteudoJSON = "application/json"

func verificaTipoDoConteudo(t *testing.T, resposta *httptest.ResponseRecorder, esperado string) {
    t.Helper()
    if resposta.Result().Header.Get("content-type") != esperado {
        t.Errorf("resposta não obteve content-type de %s, obtido %v", esperado, resposta.Result().Header)
    }
}
```

Use isso no teste.

```go
verificaTipoDoConteudo(t, resposta, tipoDoConteudoJSON)
```

Agora que nós resolvemos `ServidorJogador`, por agora podemos mudar nossa atenção para `ArmazenamentoDeJogadorNaMemoria` porque no momento se nós tentarmos demonstrá-lo para o gerente de produto, `/liga` não vai funcionar.

A forma mais rápida de nós termos alguma confiança é adicionar a nosso teste de integração, nós podemos bater no novo endpoint e checar se nós recebemos a resposta correta de `/liga`.

## Escreva o teste primeiro

Nós podemos usar `t.Run` para parar este teste um pouco e então reusar os helpers dos testes do nosso servidor - novamente mostrando a importância de refatoração dos testes.

```go
func TestGravaVitoriasEAsRetorna(t *testing.T) {
    armazenamento := NovoArmazenamentoDeJogadorNaMemoria()
    servidor := NovoServidorJogador(armazenamento)
    jogador := "Pepper"

    servidor.ServeHTTP(httptest.NewRecorder(), novaRequisiçãoPostDeVitoria(jogador))
    servidor.ServeHTTP(httptest.NewRecorder(), novaRequisiçãoPostDeVitoria(jogador))
    servidor.ServeHTTP(httptest.NewRecorder(), novaRequisiçãoPostDeVitoria(jogador))

    t.Run("obter pontuação", func(t *testing.T) {
        resposta := httptest.NewRecorder()
        servidor.ServeHTTP(resposta, novaRequisicaoObterPontuacao(jogador))
        verificaStatus(t, resposta.Code, http.StatusOK)

        verificaCorpoDaResposta(t, resposta.Body.String(), "3")
    })

    t.Run("obter liga", func(t *testing.T) {
        resposta := httptest.NewRecorder()
        servidor.ServeHTTP(resposta, novaRequisicaoDeLiga())
        verificaStatus(t, resposta.Code, http.StatusOK)

        obtido := obterLigaDaResposta(t, resposta.Body)
        esperado := []Jogador{
            {"Pepper", 3},
        }
        verificaLiga(t, obtido, esperado)
    })
}
```

## Tente rodar o teste

```
=== RUN   TestGravaVitoriasEAsRetorna/obter_liga
    --- FAIL: TestGravaVitoriasEAsRetorna/obter_liga (0.00s)
        servidor_integration_test.go:35: obtido [] esperado [{Pepper 3}]
```

## Escreva código suficiente para fazê-lo passar

`ArmazenamentoDeJogadorNaMemoria` is returning `nil` when you call `ObterLiga()` so we'll need to fix that.

```go
func (a *ArmazenamentoDeJogadorNaMemoria) ObterLiga() []Jogador {
    var liga []Jogador
    for nome, vitórias := range a.armazenamento {
        liga = append(liga, Jogador{nome, vitórias})
    }
    return liga
}
```

Tudo que nós precisamos fazer é iterar através do map e converter cada chave/valor para um `Jogador`

O teste deve passar agora.

## Concluindo

Nós temos continuado a seguramente iterar no nosso programa usando TDD, fazendo ele suportar novos endpoints de uma forma manutenível com um roteador e isso pode agora retornar JSON para nossos consumidores. No próximo capítulo, nós vamos cobrir persistência de dados e ordenação de nossas ligas.

O que nós cobrimos:

* **Roteamento**. A biblioteca padrão oferece uma fácil forma de usar tipos para fazer roteamento. Ela abraça completamente a interface `http.Handler` nela, tanto que você pode atribuir rotas para `Handler`s e a rota em si também é um `Handler`. Ela não tem alguns recursos que você pode esperar, como caminhos para variáveis (ex. `/users/{id}`). Você pode facilmente analisar esta informação por si mesmo porém você pode querer considerar olhar para outras bibliotecas de roteamento se isso se tornar um fardo. Muitas das mais populares seguem a filosofia das bibliotecas padrões e também implementam `http.Handler`.
* **Composição**. Nós tocamos um pouco nesta técnica porém você pode [ler mais sobre isso de Effective Go](https://golang.org/doc/effective_go.html#embedding). Se há uma coisa que você deve tirar disso é que composições podem ser extremamente úteis, porém *sempre pensando na sua API pública, só exponha o que é apropriado*.
* **Serialização e Desserialização de JSON**. A biblioteca padrão faz isto de forma bastante trivial ao serializar e desserializar nosso dado. Isto também abre para configurações e você pode customizar como esta transformação de dados funciona se necessário.


# IO e sorting

[**Você pode encontrar todo o código para este capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/criando-uma-aplicacao/io)

[No capitulo anterior](https://github.com/larien/learn-go-with-tests/tree/ad57fe05d7b62ac0185cdea5e47a85a60b84938d/criando-uma-aplicacao/json.md) continuamos interagindo com nossa aplicação pela adição de um novo endpoint `/liga`. Durante o caminho aprendemos como lidar com JSON, tipos embutidos e roteamento.

Nossa dona do produto está de certa forma preocupada, por conta do software perder as pontuações quando o servidor é reiniciado. Ela também não se agradou que nós não interpretamos o endpoint `/liga` que deveria retornar os jogadores ordenados pelo número de vitórias!

## O código até agora

```go
// server.go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

// GuardaJogador armazena informações sobre os jogadores
type GuardaJogador interface {
    PegaPontuacaoDoJogador(nome string) int
    SalvaVitoria(nome string)
    PegaLiga() []Jogador
}

// Jogador guarda o nome com o número de vitorias
type Jogador struct {
    Nome string
    Vitorias int
}

// ServidorDoJogador é uma interface HTTP para informações dos jogadores
type ServidorDoJogador struct {
    armazenamento GuardaJogador
    http.Handler
}

const jsonContentType = "application/json"

// NovoServidorDoJogador cria um ServidorDoJogador com roteamento configurado
func NovoServidorDoJogador(armazenamento GuardaJogador) *ServidorDoJogador {
    p := new( ServidorDoJogador)

    p.armazenamento = armazenamento

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(p.ManipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(p.ManipulaJogador))

    p.Handler = roteador

    return p
}

func (p *ServidorDoJogador) ManipulaLiga(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(p.armazenamento.PegaLiga())
    w.Header().Set("content-type", jsonContentType)
    w.WriteHeader(http.StatusOK)
}

func (p *ServidorDoJogador) ManipulaJogador(w http.ResponseWriter, r *http.Request) {
    jogador := r.URL.Path[len("/jogadores/"):]

    switch r.Method {
    case http.MethodPost:
        p.processaVitoria(w, jogador)
    case http.MethodGet:
        p.mostraPontuacao(w, jogador)
    }
}

func (p *ServidorDoJogador) mostraPontuacao(w http.ResponseWriter, jogador string) {
    pontuacao := p.armazenamento.PegaPontuacaoDoJogador(jogador)

    if pontuacao == 0 {
        w.WriteHeader(http.StatusNotFound)
    }

    fmt.Fprint(w, pontuacao)
}

func (p *ServidorDoJogador) processaVitoria(w http.ResponseWriter, jogador string) {
    p.armazenamento.salvaVitorias(jogador)
    w.WriteHeader(http.StatusAccepted)
}
```

```go
// ArmazenamentoDeJogadorNaMemoria.go
package main

func NovoArmazenamentoDeJogadorNaMemoria() *ArmazenamentoDeJogadorNaMemoria {
    return &ArmazenamentoDeJogadorNaMemoria{map[string]int{}}
}

type ArmazenamentoDeJogadorNaMemoria struct {
    armazenamento map[string]int
}

func (i *ArmazenamentoDeJogadorNaMemoria) PegaLiga() []Jogador {
    var liga []Jogador
    for nome, vitorias := range i.armazenamento {
        liga = append(liga, Jogador{nome, vitorias})
    }
    return liga
}

func (i *ArmazenamentoDeJogadorNaMemoria) SalvaVitoria(nome string) {
    i.armazenamento[nome]++
}

func (i *ArmazenamentoDeJogadorNaMemoria) PegaPontuacaoDoJogador(nome string) int {
    return i.armazenamento[nome]
}
```

```go
// main.go
package main

import (
    "log"
    "net/http"
)

func main() {
    servidor:= NovoServidorDoJogador(NovoArmazenamentoDeJogadorNaMemoria())

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("Não foi possivel ouvir na porta 5000 %v", err)
    }
}
```

Você pode encontrar todos os testes relacionados no link no começo desse capítulo.

## Armazene os dados

Existem diversos bancos de dados que poderíamos usar para isso, mas nós vamos por uma abordagem mais simples. Nós iremos armazenar os dados para essa aplicação em um arquivo como JSON.

Isso mantém os dados bastante manipuláveis e é relativamente simples de implementar.

Não será bem escalável mas, dado que isto é um protótipo, vai funcionar para agora. Se nossas circunstâncias mudarem e isto não for mais apropriado, será simples trocar para algo diferente por conta da abstração de `GuardarJogadores` que nós usamos.

Nós vamos manter o `NovoArmazenamentoDeJogadorNaMemoria` por enquanto para que os testes de integração continuem passando a medida que formos desenvolvendo nossa armazenamento. Quando estivermos confiantes que nossa implementação é suficiente para fazer os testes de integração passarem , nós iremos trocar e apagar `NovoArmazenamentoDeJogadorNaMemoria`

## Escreva os testes primeiro

Por agora você deve estar familiar com as interfaces em torno da biblioteca padrão para leitura de dados (`io.Reader`), escrita de dados (`io.Writer`) e como nós podemos usar a biblioteca padrão para testar essas funções sem ter que usar arquivos de verdade.

Para esse trabalho ser completo precisamos implementar `GuardaJogador` , então escreveremos testes para nossa armazenamento chamando os métodos que nós precisamos implementar. Começaremos com `PegaLiga`.

```go
func TestSistemaDeArquivoDeArmazenamentoDoJogador(t *testing.T) {

    t.Run("/liga de um leitor", func(t *testing.T) {
        bancoDeDados := strings.NewReader(`[
            {"Nome": "Cleo", "Vitorias": 10},
            {"Nome": "Chris", "Vitorias": 33}]`)

        armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

        recebido := armazenamento.PegaLiga()

        esperado := []Jogador{
            {"Cleo", 10},
            {"Chris", 33},
        }

        defineLiga(t, recebido, esperado)
    })
}
```

Estamos usando `strings.NewReader` que irá nos retornar um `Reader`, que é o que nosso `SistemaDeArquivoDeArmazenamentoDoJogador` irá usar para ler os dados. Em `main` abriremos um arquivo, que também é um `Reader`.

## Tente rodar o teste

```
# github.com/larien/aprenda-go-com-testes/json-and-io/v7
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:15:12: undefined: SistemaDeArquivoDeArmazenamentoDoJogador
```

## Escreva código suficiente para fazer o teste rodar e veja o retorno do erro do teste

Vamos definir `SistemaDeArquivoDeArmazenamentoDoJogador` em um novo arquivo

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {}
```

Tente de novo

```
# github.com/larien/aprenda-go-com-testes/json-and-io/v7
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:15:28: too many values in struct initializer
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:17:15: armazenamento.PegaLiga undefined (type SistemaDeArquivoDeArmazenamentoDoJogador has no field or method PegaLiga)
```

Está reclamando porque estamos passando para ele um `Reader` mas não está esperando um e não tem `PegaLiga` definida ainda.

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.Reader
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() []Jogador {
    return nil
}
```

Tente mais uma vez...

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador//league_from_a_reader
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador//league_from_a_reader (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:24: recebido [] esperado [{Cleo 10} {Chris 33}]
```

## Escreva código suficiente para fazer passar

Nós lemos JSON de um leitor antes

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() []Jogador {
    var liga []Jogador
    json.NewDecoder(f.bancoDeDados).Decode(&liga)
    return liga
}
```

O teste deve passar.

## Refatore

*Fizemos* isso antes! Nosso código de teste para o servidor tinha que decodificar o JSON da resposta.

Vamos tentar DRYando isso em uma função.

Crie um novo arquivo chamado `liga.go` e coloque isso nele.

```go
func NovaLiga(rdr io.Reader) ([]Jogador, error) {
    var liga []Jogador
    err := json.NewDecoder(rdr).Decode(&liga)
    if err != nil {
        err = fmt.Errorf("Problema parseando a liga, %v", err)
    }

    return liga, err
}
```

Chame isso em nossa implementação e em nosso teste helper `obterLigaDaResposta` in `serv_test.go`

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() []Jogador {
    liga, _ := NovaLiga(f.bancoDeDados)
    return liga
}
```

Ainda não temos a estratégia para lidar com a análise de erros mas vamos continuar.

### Procurando problemas

Existe um problema na nossa implementação. Primeiramente, vamos relembrar como `io.Reader` é definida.

```go
type Reader interface {
    Read(p []byte) (n int, err error)
}
```

Com nosso arquivo, você consegue imagina-lo lendo byte por byte até o fim. O que acontece se você tentar e `ler` uma segunda vez?

Adicione o seguinte no final do seu teste atual.

```go
// read again
recebido = armazenamento.PegaLiga()
defineLiga(t, recebido, esperado)
```

Queremos que passe, mas se você rodar o teste ele não passa.

O problema é nosso `Reader` chegou no final, então não tem mais nada para ser lido. Precisamos de um jeito de avisar para voltar ao inicio.

[ReadSeeker](https://golang.org/pkg/io/#ReadSeeker) é outra interface na biblioteca padrão que pode ajudar.

```go
type ReadSeeker interface {
    Reader
    Seeker
}
```

Lembra-se do incorporamento? Esta é uma interface composta de `Reader` e [`Seeker`](https://golang.org/pkg/io/#Seeker)

```go
type Seeker interface {
    Seek(offset int64, whence int) (int64, error)
}
```

Parece bom, podemos mudar `SistemaDeArquivoDeArmazenamentoDoJogador` para pegar essa interface no lugar?

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.ReadSeeker
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() []Jogador {
    f.bancoDeDados.Seek(0, 0)
    liga, _ := NovaLiga(f.bancoDeDados)
    return liga
}
```

Tente rodar o teste,agora passa! Ainda bem que `string.NewReader` que nós usamos em nosso teste também implementa `ReadSeeker` então não precisamos mudar nada.

A seguir vamos implementar `PegarPontuacaooDoJogador`.

## Escreva o teste primeiro

```go
t.Run("pegar pontuação do jogador", func(t *testing.T) {
    bancoDeDados := strings.NewReader(`[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

    recebido := armazenamento.("Chris")

    esperado := 33

    if recebido != esperado {
        t.Errorf("recebido %d esperado %d", recebido, esperado)
    }
})
```

## Tente rodar o teste

`./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:38:15: armazenamento. undefined (type SistemaDeArquivoDeArmazenamentoDoJogador has no field or method )`

## Escreva código suficiente para fazer o teste rodar e veja o retorno do erro do teste

Precisamos adicionar o método para o novo tipo para fazer o teste compilar.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) (nome string) int {
    return 0
}
```

Agora compila e o teste falha

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/get_player_score
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador//get_player_score (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:43: recebido 0 esperado 33
```

## Escreva código sufience para fazer passar

Podemos iterar sobre a liga para encontrar o jogador e retornar a pontuação dele.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) (nome string) int {

    var vitorias int

    for _, jogador := range f.PegaLiga() {
        if jogador.Nome == nome {
            vitorias = jogador.Vitorias
            break
        }
    }

    return vitorias
}
```

## Refatore

Você terá visto vários refatoramentos de teste helper, então deixarei este para você fazer funcionar

```go
t.Run("/pega pontuacao do  jogador", func(t *testing.T) {
    bancoDeDados := strings.NewReader(`[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

    recebido := armazenamento.("Chris")
    esperado := 33
    definePontuacaoIgual(t, recebido, esperado)
})
```

Finalmente, precisamos começar a salvar pontuações com `SalvaVitoria`.

## Escreva o teste primeiro

Nossa abordagem é um pouco ruim para escritas. Não podemos (facilmente) apenas atualizar uma "linha" de JSON em um arquivo. Precisaremos armazenar a *inteira* nova representação de nosso banco de dados em cada escrita.

Como escrevemos? Normalmente usaríamos um `Writer`, mas já temos nosso `ReadSeeker`. Potencialmente podemos ter duas dependências, mas a biblioteca padrão já tem uma interface para nós: o `ReadWriteSeeker`, que permite fazermos tudo que precisamos com um arquivo.

Vamos atualizar nosso tipo:

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.ReadWriteSeeker
}
```

Veja se compila:

```go
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:15:34: cannot use bancoDeDados (type *strings.Reader) as type io.ReadWriteSeeker in field value:
    *strings.Reader does not implement io.ReadWriteSeeker (missing Write method)
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:36:34: cannot use bancoDeDados (type *strings.Reader) as type io.ReadWriteSeeker in field value:
    *strings.Reader does not implement io.ReadWriteSeeker (missing Write method)
```

Não é tão surpreendente que `strings.Reader` não implementa `ReadWriteSeeker`, então o que vamos fazer?

Temos duas opções:

* Criar um arquivo temporário para cada teste. `*os.File` implementa `ReadWriteSeeker`. O pró disso é que isso se torna mais um teste de integração, mas nós realmente estamos lendo e escrevendo de um sistema de arquivos então isso nos dará um alto nível de confiança. Os contras são que preferimos testes unitários porque são mais rápidos e normalmente mais simples. Também precisaremos trabalhar mais criando arquivos temporários e então ter certeza que serão removidos após o teste.
* Poderíamos usar uma biblioteca externa. [Mattetti](https://github.com/mattetti) escreveu uma biblioteca [filebuffer](https://github.com/mattetti/filebuffer) que implementa a interface que precisamos e assim não precisariamos modificar o sistema de arquivos.

Não acredito que exista uma resposta especialmente errada aqui, mas ao escolher usar uma biblioteca externa eu teria que explicar o gerenciamento de dependências! Então usaremos os arquivos.

Antes de adicionarmos nosso teste precisamos fazer nossos outros testes compilarem substituindo o `strings.Reader` com um `os.File`.

Vamos criar uma função auxiliar que irá criar um arquivo temporário com alguns dados dentro dele

```go
func criaArquivoTemporario(t *testing.T, dadoInicial string) (io.ReadWriteSeeker, func()) {
    t.Helper()

   arquivotmp, err := ioutil.TempFile("", "db")

    if err != nil {
        t.Fatalf("não foi possivel escrever o arquivo temporário %v", err)
    }

    arquivotmp.Write([]byte(dadoInicial))

    removeArquivo := func() {
        arquivotmp.Close()
        os.Remove(arquivotmp.Name())
    }

    return arquivotmp, removeArquivo
}
```

[TempFile](https://golang.org/pkg/io/ioutil/#TempDir) cria um arquivo temporário para usarmos. O valor `"db"` que passamos é um prefixo colocado em um arquivo de nome aleatório que vai criar. Isto é para garantir que não vai dar conflito acidental com outros arquivos.

Você irá notar que não estamos retornando apenas nosso `ReadWriteSeeker` (o arquivo) mas também uma função. Precisamos garantir que o arquivo é removido uma vez que o teste é finalizado. Não queremos que dados sejam vazados dos arquivos no teste como é possível acontecer e desinteressante para o leitor. Ao retornar uma função `removeArquivo` , cuidamos dos detalhes no nosso auxiliar e tudo que a chamada precisa fazer é executar `defer limpaBancoDeDados()`.

```go
func TestaArmazenamentoDeSistemaDeArquivo(t *testing.T) {

    t.Run("liga de um leitor", func(t *testing.T) {
        bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
            {"Nome": "Cleo", "Vitorias": 10},
            {"Nome": "Chris", "Vitorias": 33}]`)
        defer limpaBancoDeDados()

        armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

        recebido := armazenamento.PegaLiga()

        esperado := []Jogador{
            {"Cleo", 10},
            {"Chris", 33},
        }

        defineLiga(t, recebido, esperado)

        // ler novamente
        recebido = armazenamento.PegaLiga()
        defineLiga(t, recebido, esperado)
    })

    t.Run("retorna pontuação do jogador", func(t *testing.T) {
        bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
            {"Nome": "Cleo", "Vitorias": 10},
            {"Nome": "Chris", "Vitorias": 33}]`)
        defer limpaBancoDeDados()

        armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

        recebido := armazenamento.("Chris")
        esperado := 33
        definePontuacaoIgual(t, recebido, esperado)
    })
}
```

Rode os testes e eles devem estar passando! Teve uma quantidade razoável de mudanças mas agora parece que nossa definição de interface completa e deve ser muito fáci adicionar novos testes de agora em diante.

Vamos pegar a primeira iteração de gravar uma vitória de um jogador existente

```go
t.Run("armazena vitórias de um jogador existente", func(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)
    defer limpaBancoDeDados()

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

    armazenamento.SalvaVitoria("Chris")

    recebido := armazenamento.("Chris")
    esperado := 34
    definePontuacaoIgual(t, recebido, esperado)
})
```

## Tente rodar o teste

`./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:67:8: armazenamento.SalvaVitoria undefined (type SistemaDeArquivoDeArmazenamentoDoJogador has no field or method SalvaVitoria)`

## Escreva código suficiente para fazer o teste rodar e veja o retorno do erro do teste

Adicione um novo método

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {

}
```

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/store_wins_for_existing_players
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador/store_wins_for_existing_players (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:71: recebido 33 esperado 34
```

Nossa implementação está vazia então a pontuação anterior está sendo retornada.

## Escreva código sufience para fazer passar

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {
    liga := f.PegaLiga()

    for i, jogador := range liga {
        if jogador.Nome == nome {
            liga[i].Vitorias++
        }
    }

    f.bancoDeDados.Seek(0,0)
    json.NewEncoder(f.bancoDeDados).Encode(liga)
}
```

Você deve está se perguntando por que estou fazendo `liga[i].Vitorias++` invés de `jogador.Vitorias++`.

Quando você `percorre` sobre um pedaço é retornado o índice atual do laço (no nosso caso `i`) e uma *cópia* do elemento naquele índice. Mudando o valor `Vitorias` não irá afetar no pedaço `liga` que iteramos sobre. Por este motivo, precisamos pegar a referência do valor atual fazendo `liga[i]` e então mudando este valor.

Se rodar os testes, eles devem estar passando.

## Refatore

Em `PegaPontuacaoDoJogador` e `SalvaVitoria`, estamos iterando sobre `[]Jogador` para encontrar um jogador pelo nome.

Poderíamos refatorar esse código comum nos internos de `SistemaDeArquivoDeArmazenamentoDoJogador` mas para mim, parece que talvez seja um código util então poderíamos colocar em um novo tipo. Trabalhando com uma "Liga" até agora tem sido com `[]Jogador` mas podemos criar um novo tipo chamado `Liga`. Será mais fácil para outros desenvolvedores entenderem e assim podemos anexar métodos utéis dentro desse tipo para usarmos.

Dentro de `liga.go` adicionamos o seguinte

```go
type Liga []Jogador
func (l Liga) Find(nome string) *Jogador {
    for i, p := range l {
        if p.Nome==nome {
            return &l[i]
        }
    }
    return nil
}
```

Agora se qualquer um tiver uma `Liga` facilmente será encontrado um dado jogador.

Mude nossa interface `GuardaJogador` para retornar `Liga` invés de `[]Jogador`. Tente e rode novamente os teste, você terá um problema de compilação por termos modificado a interface mas é fácil de resolver; apenas modifique o tipo de retorno de `[]Jogador` to `Liga`.

Isso nos permite simplificar os métodos em `SistemaDeArquivoDeArmazenamentoDoJogador`.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) (nome string) int {

    jogador := f.PegaLiga().Find(nome)

    if  jogador != nil {
        return  jogador.Vitorias
    }

    return 0
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {
    liga := f.PegaLiga()
    jogador :=liga.Find(nome)

    if  jogador != nil {
        jogador.Vitorias++
    }

    f.bancoDeDados.Seek(0, 0)
    json.NewEncoder(f.bancoDeDados).Encode(liga)
}
```

Isto parece bem melhor and podemos ver como talvez possamos encontrar como outras funcionalidades úteis em torno de `Liga` podem ser refatoradas.

Agora precisamos tratar o cenário de salvar vitórias de novos jogadores.

## Escreva o teste primeiro

```go
t.Run("armazena vitorias de novos jogadores", func(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)
    defer limpaBancoDeDados()

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

    armazenamento.SalvaVitoria("Pepper")

    recebido := armazenamento.("Pepper")
    esperado := 1
    definePontuacaoIgual(t, recebido, esperado)
})
```

## Tente rodar o teste

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/store_wins_for_new_players#01
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador/store_wins_for_new_players#01 (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:86: recebido 0 esperado 1
```

## Escreva código suficiente para fazer passar

Apenas precisamos tratar o caso onde `Find` returna `nil` por não ter conseguido encontrar o jogador.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {
    liga := f.PegaLiga()
    jogador := liga.Find(nome)

    if jogador != nil {
        jogador.Wins++
    } else {
        liga = append(liga, Jogador{nome, 1})
    }

    f.bancoDeDados.Seek(0, 0)
    json.NewEncoder(f.bancoDeDados).Encode(liga)
}
```

O caminho feliz parece bom então agora vamos tentar usar nossa nova `armazenamento` no teste de integração. Isto nos dará mais confiança que o software funciona e então podemos deletar o redundante `NovoArmazenamentoDeJogadorNaMemoria`.

Em `TestRecordingWinsAndRetrievingThem` substitui a velha armazenamento.

```go
bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, "")
defer limpaBancoDeDados()
armazenamento := &SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}
```

Se você rodar o teste ele deve passar e agora podemos deletar `NovoArmazenamentoDeJogadorNaMemoria`. `main.go` terá problemas de compilação que nos motivará para agora usar nossa nova armazenamento no código "real".

```go
package main

import (
    "log"
    "net/http"
    "os"
)

const dbFileName = "game.db.json"

func main() {
    db, err := os.OpenFile(dbFileName, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        log.Fatalf("problema abrindo %s %v", dbFileName, err)
    }

    armazenamento := &SistemaDeArquivoDeArmazenamentoDoJogador{db}
    server := NovoServidorDoJogador(armazenamento)

    if err := http.ListenAndServe(":5000", server); err != nil {
        log.Fatalf("não foi possivel escutar na porta 5000 %v", err)
    }
}
```

* Nós criamos um arquivo para nosso banco de dados.
* O 2º argumento para `os.OpenFile` permite definir as permissões para abrir um arquivo, no nosso caso `O_RDWR` significa que queremos ler e escrever *e* `os.O_CREATE` significa criar um arquivo se ele não existe.
* O 3º argumento significa definir as permissões para o arquivo, no nosso caso, todos os usuários podem ler e escrever o arquivo. [(Veja superuser.com para uma explicação mais detalhada)](https://superuser.com/questions/295591/what-is-the-meaning-of-chmod-666).

Rodando o programa agora os dados permanecem em um arquivo entre reinicializações, uhu!

## Mais refatoramento e preocupações com performance

Toda vez que alguém chama `PegaLiga()` ou `()` estamos lendo o arquivo do ínicio, e transformando ele em JSON. Não deveríamos ter que fazer isso porque `SistemaDeArquivoDeArmazenamentoDoJogador` é inteiramente responsável pelo estado da liga; apenas queremos usar o arquivo para pegar o estado atual e atualiza-lo quando os dados mudarem.

Podemos criar um construtor que pode fazer parte dessa inicialização para nós e armazena a liga como um valor em nosso `SistemaDeArquivoDeArmazenamentoDoJogador` para ser usado nas leitura então.

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.ReadWriteSeeker
    liga Liga
}

func NovoSistemaDeArquivoDeArmazenamentoDoJogador(bancoDeDados io.ReadWriteSeeker) *SistemaDeArquivoDeArmazenamentoDoJogador {
    bancoDeDados.Seek(0, 0)
    liga, _ := NovaLiga(bancoDeDados)
    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados:bancoDeDados,
        liga:liga,
    }
}
```

Desta maneira precisamos ler do disco apenas uma vez . Podemos agora substituir todas as nossas chamadas anteriores para pegar a liga do disco e apenas usar `f.liga` no lugar.

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() Liga {
    return f.liga
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) (nome string) int {

    jogador := f.liga.Find(nome)

    if jogador != nil {
        return jogador.Vitorias
    }

    return 0
}

func (f *SistemaDeArquivoDeArmazenamentoDoJogador) SalvaVitoria(nome string) {
    jogador := f.liga.Find(nome)

    if jogador != nil {
        jogador.Vitorias++
    } else {
        f.liga = append(f.liga, Jogador{nome, 1})
    }

    f.bancoDeDados.Seek(0, 0)
    json.NewEncoder(f.bancoDeDados).Encode(f.liga)
}
```

Se você tentar e rodar os testes eles agora vão reclamar sobre inicializar `SistemaDeArquivoDeArmazenamentoDoJogador` então fixe-o chamando nosso construtor.

### Outro problema

Existe mais alguma ingenuidade na maneira como estamos lidando com arquivos que *poderiamos* criar um erro bem bobo futuramente.

Quando nós chamamos `SalvaVitoria` nós `procuramos` no ínicio do arquivo e então escrevemos o novo dado mas e se o novo dado for menor que o que estava lá antes?

Na nossa situação atual, isso é impossível. Nunca editamos ou apagamos pontuações, então os dados apenas podem aumentar, mas seria irresponsabilidade nossa deixar o código desse jeito, não é inimaginável que um cenário de apagamento poderia aparecer.

Como iremos testar isso então? O que precisamos fazer primeiro é refatorar nosso código, então separamos nossa preocupação do *tipo de dados que escrevemos, da escrita*. Podemos então testar isso separadamente para verificar se funciona como esperamos.

Agora iremos criar um novo tipo para encapsular nossa funcionalidade "quando escrevemos, vamos para o começo". Vou chama-la de `Fita`. Criamos um novo arquivo com o seguinte

```go
package main

import "io"

type fita struct {
    arquivo io.ReadWriteSeeker
}

func (t *fita) Write(p []byte) (n int, err error) {
    t.arquivo.Seek(0, 0)
    return t.arquivo.Write(p)
}
```

Note que apenas implementamos `Write` agora, já que encapsula a parte de `Procura` . Isso que dizer que `SistemaDeArquivoDeArmazenamentoDoJogador` pode ter uma referência a `Writer` invés disso.

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados io.Writer
    liga   Liga
}
```

Atualize o construtor para usar `fita`

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(bancoDeDados io.ReadWriteSeeker) *SistemaDeArquivoDeArmazenamentoDoJogador {
    bancoDeDados.Seek(0, 0)
    liga, _ := NovaLiga(bancoDeDados)

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: &fita{bancoDeDados},
        liga:   liga,
    }
}
```

Finalmente, podemos ter o incrível beneficio que queríamos removendo `Procura` de `SalvaVitoria`. Sim, não parece muito, mas pelo menos isso significa que, se fizermos qualquer outro tipo de escritas, podemos confiar no nosso `Write` para se comportar como precisamos. Além disso, agora podemos testar o potencial código problemático separadamente e corrigi-lo.

Agora vamos escrever o teste onde atualizamos todo o conteúdo de um arquivo com algo menor que o conteúdo original . Em `fita_test.go`:

## Escreva o teste primeiro

Vamos apenas criar um arquivo, tentar e escrever nele usando nossa fita, ler todo novamente e visualizar o que está no arquivo

```go
func TestaFita_Escrita(t *testing.T) {
    arquivo, limpa := criaArquivoTemporario(t, "12345")
    defer limpa()

    fita := &fita{arquivo}

    fita.Write([]byte("abc"))

    arquivo.Seek(0, 0)
    novoConteudoDoArquivo, _ := ioutil.ReadAll(arquivo)

    recebido := string(novoConteudoDoArquivo)
    esperado := "abc"

    if recebido != esperado {
        t.Errorf("recebido '%s' esperado '%s'", recebido, esperado)
    }
}
```

## Tente rodar o teste

```
=== RUN   TestaFita_Escrita
--- FAIL: TestaFita_Escrita (0.00s)
    fita_test.go:23: recebido 'abc45' esperado 'abc'
```

Como pensamos! Ele apenas escreve os dados que queremos, deixando todo o resto.

## Escreva código suficiente para fazer passar

`os.File` tem uma função truncada que vai permitir que o arquivo seja esvaziado eficientemente. Devemos ser capazes de apenas chama-la para conseguir o que queremos.

Mude `fita` para o seguinte

```go
type fita struct {
    file *os.File
}

func (t *fita) Write(p []byte) (n int, err error) {
    t.file.Truncate(0)
    t.file.Seek(0, 0)
    return t.file.Write(p)
}
```

O compilador irá falhar em alguns lugares quando esperamos um `io.ReadWriteSeeker` mas estamos mandando um `*os.File`. Você deve ser capaz de corrigir esses problemas por conta própria, mas se ficar preso basta checar o código fonte.

Uma vez que você tenha refatorado nosso teste `TestaFita_Escrita` deve estar passando!

### Uma outra pequena refatoração

Em `SalvaVitoria` temos uma linha`json.NewEncoder(f.bancoDeDados).Encode(f.league)`.

Não precisamos criar um novo codificador toda vez que escrevemos, podemos inicializar um em nosso construtor e usa-lo.

Armazena uma referência para um `Encoder` para nosso tipo.

```go
type SistemaDeArquivoDeArmazenamentoDoJogador struct {
    bancoDeDados *json.Encoder
    liga   Liga
}
```

Inicialize no construtor

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(arquivo *os.File) *SistemaDeArquivoDeArmazenamentoDoJogador {
    arquivo.Seek(0, 0)
    liga, _ := NovaLiga(arquivo)

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: json.NewEncoder(&fita{arquivo}),
        liga:   liga,
    }
}
```

Use em `SalvaVitoria`.

## Não quebramos algumas regras ali? Testando coisas privadas? Sem interfaces?

### Testando tipos privados

É verdade que *no geral* deve ser favorecido não testar coisas privadas, uma vez que isso, as vezes, leva a testar coisas bastante acopladas para a implementação; que pode impedir refatoramento no futuro.

Entretanto,não devemos esquecer que testes nos dá *confiança*.

Não estamos confiantes que nossa implementação funcionaria se tivéssemos adicionado algum tipo de funcionalidade para editar ou deletar. Não queremos deixar o código assim, especialmente se isso foi trabalhado por mais de uma pessoa que talvez não estivesse ciente dos defeitos da nossa abordagem.

Finalmente, é apenas um teste! Se decidirmos mudar a maneira como funciona não será um desastre deletar o teste, mas teremos que ter pego o requisito para futuro mantenedores.

### Interfaces

Começamos o código usando `io.Reader` como o caminho mais fácil para testar de forma unitária nosso novo `GuardaJogador`. A medida que desenvolvemos nosso código, movemos para `io.ReadWriter` e então para `io.ReadWriteSeeker`. Descobrimos então que não tinha nada na biblioteca padrão que implementasse isso além de `*os.File`. Poderiamos ter decidido escrever o nosso ou usar um de código aberto, mas isso pareceu pragmático apenas para fazer arquivos temporários para os testes.

Finalmente, precisamos de `Truncate` que também está no `*os.File`. Isso seria uma opção para criar nossa própria interface pegando esses requisitos.

```go
type ReadWriteSeekTruncate interface {
    io.ReadWriteSeeker
    Truncate(size int64) error
}
```

Mas o que isso está realmente nos dando? Lembre-se que *não estamos mockando* e isso é irrealista para um armazenamento de **sistema de arquivos** receber outro tipo além que um `*os.File` então não precisamos do polimorfismo que interface nos dá.

Não tenha medo de cortar e mudar tipos e experimentar como temos aqui. O bom de usar uma linguagem tipada estaticamente é o compilador que ajudará você com toda mudança.

## Tratamento de erros

Antes de começarmos no ordenamento, devemos ter certeza que estamos contentes com nosso código atual e remover qualquer débito técnico que ainda resta. É um principio importante para trabalhar com software o mais rápido possível (mantenha-se fora do estado vermelho) mas isso não quer dizer que devemos ignorar os casos de erro!

Se voltarmos para `SistemaDeArquivoDeArmazenamentoDoJogador.go` temos `liga, _ := NovaLiga(f.bancoDeDados)` no nosso construtor.

`NovaLiga`pode retornar um erro se é instável passar a liga do `io.Reader` que fornecemos.

Era pragmático ignorar isso naquela hora como já tinhamos testes falhando. Se tivemos tentado lidar com isso ao mesmo tempo estamos lidando com duas coisas de uma vez.

Vamos fazer com que nosso construtor seja capaz de retornar um erro.

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(arquivo *os.File) (*SistemaDeArquivoDeArmazenamentoDoJogador, error) {
    arquivo.Seek(0, 0)
    liga, err := NovaLiga(arquivo)

    if err != nil {
        return nil, fmt.Errorf("problema carregando o armazenamento do jogador  de arquivo %s, %v", arquivo.Nome(), err)
    }

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: json.NewEncoder(&fita{arquivo}),
        liga:   liga,
    }, nil
}
```

Lembre-se que é importante retornar mensagens de erro úteis (assim como nossos testes). As pessoas na internet dizem que a maioria dos códigos em Go é

```go
if err != nil {
    return err
}
```

**Isso é 100% não idiomático.** Adicionando informação contextual (i.e o que você estava fazendo que causou o erro\\) para suas mensagens de erro facilita manipular o software.

Se você tentar e compilar, vai ver alguns erros.

```
./main.go:18:35: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:35:36: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:57:36: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:70:36: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./SistemaDeArquivoDeArmazenamentoDoJogador_test.go:85:36: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
./server_integration_test.go:12:35: multiple-value NovoSistemaDeArquivoDeArmazenamentoDoJogador() in single-value context
```

Em main vamos querer sair do programa, imprimindo o erro.

```go
armazenamento, err := NovoSistemaDeArquivoDeArmazenamentoDoJogador(db)

if err != nil {
    log.Fatalf("problema criando o sistema de arquivo do armazenamento do jogador, %v ", err)
}
```

Nos nossos testes podemos garantir que não exista erro . Podemos fazer uma função auxiliar para ajudar com isto.

```go
func defineSemErro(t *testing.T, err error) {
    t.Helper()
    if err != nil {
        t.Fatalf("não esperava um erro mas obteve um, %v", err)
    }
}
```

Trabalhe nos outros problemas de compilação usando essa auxiliar. Finalmente, você deve ter um teste falhando

```
=== RUN   TestRecordingWinsAndRetrievingThem
--- FAIL: TestRecordingWinsAndRetrievingThem (0.00s)
    server_integration_test.go:14: não esperava um erro mas obteve um, problema carregando o armazenamento do jogador  de arquivo /var/folders/nj/r_ccbj5d7flds0sf63yy4vb80000gn/T/db841037437, problem parsing league, EOF
```

Não podemos analisar a liga porque o arquivo está vazio.Não estávamos obtendo erros antes porque sempre os ignoramos.

Vamos corrigir nosso grande teste de integração colocando algum JSON válido nele e então podemos escrever um teste específico para este cenário.

```go
func TestRecordingWinsAndRetrievingThem(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[]`)
    //etc...
```

Agora todos os testes estão passando, precisamos então lidar com o cenário onde o arquivo está vazio.

## Escreva o teste primeiro

```go
t.Run("funciona com um arquivo vazio", func(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, "")
    defer limpaBancoDeDados()

    _, err := NovoSistemaDeArquivoDeArmazenamentoDoJogador(bancoDeDados)

    defineSemErro(t, err)
})
```

## Tente rodar o teste

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/works_with_an_empty_file
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador/works_with_an_empty_file (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:108: não esperava um erro mas obteve um, problema carregando o armazenamento do jogador  de arquivo /var/folders/nj/r_ccbj5d7flds0sf63yy4vb80000gn/T/db019548018, problem parsing league, EOF
```

## Escreva código sufience para fazer passar

Mude nosso construtor para o seguinte

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(arquivo *os.File) (*SistemaDeArquivoDeArmazenamentoDoJogador, error) {

    arquivo.Seek(0, 0)

    info, err := arquivo.Stat()

    if err != nil {
        return nil, fmt.Errorf("problema ao usar o arquivo  %s, %v", arquivo.Nome(), err)
    }

    if info.Size() == 0 {
        file.Write([]byte("[]"))
        file.Seek(0, 0)
    }

    liga, err := NovaLiga(file)

    if err != nil {
        return nil, fmt.Errorf("problema carregando armazenamento de jogador do aquivo %s, %v", arquivo.Nome(), err)
    }

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: json.NewEncoder(&fita{file}),
        liga:   liga,
    }, nil
}
```

`Arquivo.Stat` retorna estatísticas do nosso arquivo. Isto nos permite checar o tamanho do arquivo, se está vazio podemos `Escrever` um array JSON vazio e `Busca` de volta para o ínicio, pronto para o resto do arquivo.

## Refatore

Nosso construtor está um pouco bagunçado, podemos extrair o código de inicialização em uma função

```go
func iniciaArquivoBDDeJogador(arquivo *os.File) error {
    arquivo.Seek(0, 0)

    info, err := arquivo.Stat()

    if err != nil {
        return fmt.Errorf("problema ao usar arquivo %s, %v", file.Name(), err)
    }

    if info.Size()==0 {
        arquivo.Write([]byte("[]"))
        arquivo.Seek(0, 0)
    }

    return nil
}
```

```go
func NovoSistemaDeArquivoDeArmazenamentoDoJogador(arquivo *os.File) (*SistemaDeArquivoDeArmazenamentoDoJogador, error) {

    err := iniciaArquivoBDDeJogador(file)

    if err != nil {
        return nil, fmt.Errorf("problema inicializando arquivo do jogador, %v", err)
    }

    liga, err := Nova(liga)

    if err != nil {
        return nil, fmt.Errorf("problema carregando armazenamento de jogador do arquivo %s, %v", arquivo.Nome(), err)
    }

    return &SistemaDeArquivoDeArmazenamentoDoJogador{
        bancoDeDados: json.NewEncoder(&fita{file}),
        liga:   liga,
    }, nil
}
```

## Ordenação

Nossa dona do produto quer que `/liga` retorne os jogadores ordenados pela pontuação.

A principal decisão a ser feita é onde isso deve acontecer no software. Se estamos usando um "verdadeiro" banco de dados usariamos coisas como `ORDER BY` , então o ordenamento é super rápido por esse motivo parece que a implementção de `GuardaJogador` deve ser responsável.

## Escreva o teste primeiro

Podemos atualizar a inserção no nosso primeiro teste em `TestaArmazenamentoDeSistemaDeArquivo`

```go
t.Run("liga ordernada", func(t *testing.T) {
    bancoDeDados, limpaBancoDeDados := criaArquivoTemporario(t, `[
        {"Nome": "Cleo", "Vitorias": 10},
        {"Nome": "Chris", "Vitorias": 33}]`)
    defer limpaBancoDeDados()

    armazenamento := SistemaDeArquivoDeArmazenamentoDoJogador{bancoDeDados}

   recebido := armazenamento.PegaLiga()

   esperado:= []Jogador{
        {"Chris", 33},
        {"Cleo", 10},
    }

    defineLiga(t, recebido, esperado)

    // read again
    recebido = armazenamento.PegaLiga()
    defineLiga(t, recebido, esperado)
})
```

A ordem que está sendo recebida do JSON está errada e nosso `esperado` vai checar que é retornado para o chamador na ordem correta.

## Tente rodar o teste

```
=== RUN   TestSistemaDeArquivoDeArmazenamentoDoJogador/league_from_a_reader,_sorted
    --- FAIL: TestSistemaDeArquivoDeArmazenamentoDoJogador/league_from_a_reader,_sorted (0.00s)
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:46: recebido [{Cleo 10} {Chris 33}] esperado [{Chris 33} {Cleo 10}]
        SistemaDeArquivoDeArmazenamentoDoJogador_test.go:51: recebido [{Cleo 10} {Chris 33}] esperado [{Chris 33} {Cleo 10}]
```

## Escreva código sufience para fazer passar

```go
func (f *SistemaDeArquivoDeArmazenamentoDoJogador) PegaLiga() League {
    sort.Slice(f.liga, func(i, j int) bool {
        return f.liga[i].Vitorias > f.liga[j].Vitorias
    })
    return f.liga
}
```

[`sort.Slice`](https://golang.org/pkg/sort/#Slice)

> Slice ordena a parte fornecida dada a menor função fornecida

Moleza!

## Finalizando

### O que cobrimos

* A interface `Seeker` e sua relação com `Reader` e `Writer`.
* Trabalhando com arquivos.
* Criando uma auxiliar fácil de usar para testes com arquivos que escondem todas as bagunças.
* `sort.Slice` para ordenar partes.
* Usando o compilador para nos ajudar a fazer mudanças estruturais de forma segura na aplicação.

### Quebrando regras

* Maior partes das regras em engenharia de software não são realmente regras, apenas boas práticas que funcionam 80% do tempo.
* Descobrimos um cenário onde nos "regras" anteriores de não testar funções internas não foi útil, então quebramos essa regra.
* É importante entender o que estamos perdendo e ganhado ao quebrar as regras . No nosso caso, não tinha problema porque era apenas um teste e seria muito difícil exercitar o cenário contrário.
* Para poder quebrar as regras, **você deve entende-las**. Uma analogia é com aprender a tocar violão. Não importa quão criativo você seja, você deve entender e praticar os fundamentos.

### Onde nosso software está

* Temos uma API HTTP onde é possível criar jogadores e aumentar a pontuação deles..
* Podemos retornar uma liga das pontuações de todos como JSON.
* O dado é mantindo com um arquivo JSON.


# Linha de comando e estrutura de pacotes

[**Você pode encontrar os exemplos deste capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/criando-uma-aplicacao/linha-de-comando)

Nosso gerente de produto quer [*pivotar*](https://pt.wikipedia.org/wiki/Startup#Dicion%C3%A1rio_com_os_termos_mais_usados_pelas_startups) e introduzir uma segunda aplicação - uma aplicação de linha de comando.

Inicialmente, ela vai apenas ser capaz de gravar o que um jogador vence quando o usuário digita `Ruth venceu`. A intenção é eventualmente criar uma ferramenta para ajudar usuários a jogar pôquer.

O gerente de produto quer que o banco de dados seja compartilhado entre as duas aplicações para que a `liga` atualize de acordo com as vitórias gravadas nessa nova aplicação.

## Lembrando do código

Nós temos uma aplicação com um arquivo `main.go` que inicia um servidor HTTP. O servidor HTTP não é nosso interesse neste exercício mas a abstração usada é. Ele depende de `ArmazenamentoJogador`.

```go
type ArmazenamentoJogador interface {
    ObterPontuacaoDeJogador(nome string) int
    GravarVitoria(nome string)
    ObterLiga() Liga
}
```

No capítulo anterior, criamos um `SistemaDeArquivoArmazenamentoJogador` que implementa essa mesma interface. Temos que poder reutilizar parte dela para a nossa nova aplicação.

## Primeiro vamos [refatorar](https://pt.wikipedia.org/wiki/Refatora%C3%A7%C3%A3o) um pouco

Nosso projeto precisa criar dois executáveis, nosso existente servidor web e o app de linha de comando.

Antes de nos entretermos no nosso novo código, precisamos estruturar nosso projeto melhor para suportar isso.

Até agora todos os códigos foram colocador em uma única pasta, em uma estrutura parecida com essa

`$GOPATH/src/github.com/seu-nome/meu-app`

Para fazer qualquer aplicação em Go, é necessário uma função `main` dentro de um `package main`. Até agora todo nosso código viveu dentro de `package main` e a função `func main` pode referenciar tudo.

Isso foi legal e é uma boa prática não sair gerando estrutura com pacotes logo de início. Se você olhar dentro da biblioteca padrão você vai ver bem pouco a utilização de pastas e estruturas.

Felizmente é bem fácil adicionar uma estrutura *quando precisar dela*.

Dentro do projeto existente crie uma pasta `cmd` com uma chamada `webserver` dentro dela (ex: `mkdir -p cmd/webserver`).

Mova o arquivo `main.go` para dentro dessa pasta.

Se você tiver o comando `tree` instalado você pode executar sua estrutura de pastas tem que parecer

```
.
├── ArmazenamentoSistemaArquivo.go
├── ArmazenamentoSistemaArquivo_test.go
├── cmd
│   └── webserver
│       └── main.go
├── liga.go
├── servidor.go
├── servidor_integration_test.go
├── servidor_test.go
├── tape.go
└── tape_test.go
```

Agora temos uma separação efetiva entre nossa aplicação e o código da biblioteca mas agora temos que mudar alguns nomes de pacotes(package). Lembre-se que ao construir uma aplicação Go seu nome *deve* ser `main`.

Mude todos os outros códigos para ter um pacote chamado `poquer`.

Finalmente, temos que importar esse pacote no `main.go` para utilizá-lo na criação de nosso servidor web. Então podemos usar nossa biblioteca chamando `poquer.NomeDaFunção`.

Os caminhos de diretórios vão ser diferentes no seu computador, mas deveria parecer com isso:

```go
package main

import (
    "log"
    "net/http"
    "os"
    "github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v1"
)

const nomeArquivoBD = "jogo.db.json"

func main() {
    db, err := os.OpenFile(nomeArquivoBD, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        log.Fatalf("falha ao abrir %s %v", nomeArquivoBD, err)
    }

    armazenamento, err := poquer.NovoArmazenamentoSistemaDeArquivodeJogador(db)

    if err != nil {
        log.Fatalf("falha ao criar sistema de arquivos para armazenar jogadores, %v ", err)
    }

    servidor := poquer.NovoServidorJogador(armazenamento)

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("nao foi possivel escutar na porta 5000 %v", err)
    }
}
```

O caminho da pasta pode parecer chocante, mas essa é a forma para importar *qualquer* biblioteca pública no seu código.

Separando nosso código em um pacote isolado e enviando para um repositório público como o GitHub qualquer desenvolvedor Go pode escrever código que importe esse pacote com as funcionalidades que disponibilizarmos. A primeira vez que você tentar e executar ele vai reclamar que o pacote não existe mas tudo que precisa ser feito é executar `go get`.

[Além disso, usuários podem ver a documentação em godoc.org](https://godoc.org/github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v1).

### Verificações finais

* Dentro do diretório raiz rode `go test` e valide que ainda está passando
* Vá dentro de `cmd/webserver` e rode `go run main.go`
  * Abra `http://localhost:5000/liga` e veja que ainda está funcionando

### Estrutura inicial

Antes de escrever os testes, vamos adicionar uma nova aplicação que nosso projeto vai construir. Crie outro diretório dentro de `cmd` chamado `cli` (command line interface) e adicione um arquivo `main.go` com

```go
package main

import "fmt"

func main() {
    fmt.Println("Vamos jogar poquer")
}
```

O primeiro requisito que vamos discutir is como gravar uma vitória quando o usuário digitar `{NomeDoJogador} venceu`.

## Escreva o teste antes

Sabemos que temos que escrever algo chamado `CLI` que vai nos permitir `Jogar` poquer. Isso vai precisar ler o que o usuário digita e então gravar a vitória no armazenamento `ArmazenamentoJogador`.

Antes de irmos muito longe, vamos apenas escrever um teste para verificar a integração com a `ArmazenamentoJogador` funciona como gostaríamos.

Dentro de `CLI_test.go` (no diretório raiz do projeto, não dentro de `cmd`)

```go
func TestCLI(t *testing.T) {
    armazenamentoJogador := &EsbocoArmazenamentoJogador{}
    cli := &CLI{armazenamentoJogador}
    cli.JogarPoquer()

    if len(armazenamentoJogador.ChamadasDeVitoria) !=1 {
        t.Fatal("esperando uma chamada de vitoria mas nao recebi nenhuma")
    }
}
```

* Podemos usar nossa `EsbocoArmazenamentoJogador` de outros testes
* Passamos nossa dependência dentro do nosso ainda não existente tipo `CLI`
* Iniciamos o jogo chamando um método que chamaremos de `JogarPoquer`
* Validamos se a vitória foi registrada

## Tente rodar o teste

```
# github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v2
./cli_test.go:25:10: undefined: CLI
```

## Escreva o mínimo código para o teste rodar e verificarmos o próximo error

Neste ponto, você deveria estar confortável para criar nossa nova `CLI` struct (estrutura de dados) com os respectivos campos necessários para nossa dependência e adicionar um método.

Você deveria acabar com um código como esse

```go
type CLI struct {
    armazenamentoJogador ArmazenamentoJogador
}

func (cli *CLI) JogarPoquer() {}
```

Lembre-se que estamos apenas tentando fazer o teste rodar para validarmos que ele falha como esperamos

```
--- FAIL: TestCLI (0.00s)
    cli_test.go:30: esperando uma chamada de vitoria mas nao recebi nenhuma
FAIL
```

## Escreva código suficiente para fazer ele passar

```go
func (cli *CLI) JogarPoquer() {
    cli.armazenamentoJogador.GravarVitoria("Cleo")
}
```

Isso deve fazer ele passar.

Agora, precisamos simular lendo isso from `Stdin` (o que o usuário digita) para que fique registrado vitórias para jogadores específicos.

Vamos incrementar nosso teste para exercitar essa condição.

## Escreva o teste antes

```go
func TestCLI(t *testing.T) {
    in := strings.NewReader("Chris venceu\n")
    armazenamentoJogador := &EsbocoArmazenamentoJogador{}

    cli := &CLI{armazenamentoJogador, in}
    cli.JogarPoquer()

    if len(armazenamentoJogador.ChamadasDeVitoria) < 1 {
        t.Fatal("esperando uma chamada de vitoria mas nao recebi nenhuma")
    }

    obtido := armazenamentoJogador.ChamadasDeVitoria[0]
    esperado := "Chris"

    if obtido != esperado {
        t.Errorf("nao armazenou o vencedor correto, recebi '%s', esperava '%s'", obtido, esperado)
    }
}
```

`os.Stdin` é o que vamos usar no `main` para capturar o que for digitado pelo usuário. Ele é um `*File` por trás dos panos o que siginifica que implementa `io.Reader` o qual sabemos ser um jeito útil de capturar texto.

Nós criamos um `io.Reader` no nosso teste usando `strings.NewReader`, preenchendo ele com o que esperamos que o usuário digite.

## Tente rodar o teste

`./CLI_test.go:12:32: too many values in struct initializer`

Muitos valores no inicializador da estrutura.

## Escreva o mínimo código para o teste rodar e verificarmos o próximo error

Precisamos adicionar nossa nova dependência dentro de `CLI`.

```go
type CLI struct {
    armazenamentoJogador ArmazenamentoJogador
    in io.Reader
}
```

## Escreva código suficiente para fazer ele passar

```
--- FAIL: TestCLI (0.00s)
    CLI_test.go:23: nao armazenou o vencedor correto, recebi 'Cleo', esperava 'Chris'
FAIL
```

Lembre-se de primeiro fazer o que for mais fácil

```go
func (cli *CLI) JogarPoquer() {
    cli.armazenamentoJogador.GravarVitoria("Chris")
}
```

O teste vai passar. Depois nós vamos adicionar outro teste que vai nos forçar a escrever mais código, mas antes, vamos [refatorar](https://pt.wikipedia.org/wiki/Refatora%C3%A7%C3%A3o).

## [Refatoração](https://pt.wikipedia.org/wiki/Refatora%C3%A7%C3%A3o)

No `server_test` anteriormente fizemos validações para saber se uma vitória é armazenada assim como temos aqui. Vamos mover essa validação para dentro de um helper e manter o código [DRY](https://pt.wikipedia.org/wiki/Don%27t_repeat_yourself).

```go
func verificaVitoriaJogador(t *testing.T, armazenamento *EsbocoArmazenamentoJogador, vencedor string) {
    t.Helper()

    if len(armazenamento.ChamadasDeVitoria) != 1 {
        t.Fatalf("recebi %d chamadas de GravarVitoria esperava %d", len(armazenamento.ChamadasDeVitoria), 1)
    }

    if armazenamento.ChamadasDeVitoria[0] != vencedor {
        t.Errorf("nao armazenou o vencedor correto, recebi '%s' esperava '%s'", armazenamento.ChamadasDeVitoria[0], vencedor)
    }
}
```

Agora troque a validação em ambos os arquivos `server_test.go` e `CLI_test.go`.

O teste deve agora parecer com

```go
func TestCLI(t *testing.T) {
    in := strings.NewReader("Chris venceu\n")
    armazenamentoJogador := &EsbocoArmazenamentoJogador{}

    cli := &CLI{armazenamentoJogador, in}
    cli.JogarPoquer()

    verificaVitoriaJogador(t, armazenamentoJogador, "Chris")
}
```

Agora vamos escrever *outro* teste com uma variação do que o usuário digitou nos forçando a ler de verdade.

## Escreva o teste antes

```go
func TestCLI(t *testing.T) {

    t.Run("recorda vencedor chris digitado pelo usuario", func(t *testing.T) {
        in := strings.NewReader("Chris venceu\n")
        armazenamentoJogador := &EsbocoArmazenamentoJogador{}

        cli := &CLI{armazenamentoJogador, in}
        cli.JogarPoquer()

        verificaVitoriaJogador(t, armazenamentoJogador, "Chris")
    })

    t.Run("recorda vencedor cleo digitado pelo usuario", func(t *testing.T) {
        in := strings.NewReader("Cleo venceu\n")
        armazenamentoJogador := &EsbocoArmazenamentoJogador{}

        cli := &CLI{armazenamentoJogador, in}
        cli.JogarPoquer()

        verificaVitoriaJogador(t, armazenamentoJogador, "Cleo")
    })

}
```

## Tente rodar o teste

```
=== RUN   TestCLI
--- FAIL: TestCLI (0.00s)
=== RUN   TestCLI/recorda_vencedor_chris_digitado_pelo_usuario
    --- PASS: TestCLI/recorda_vencedor_chris_digitado_pelo_usuario (0.00s)
=== RUN   TestCLI/recorda_vencedor_cleo_digitado_pelo_usuario
    --- FAIL: TestCLI/recorda_vencedor_cleo_digitado_pelo_usuario (0.00s)
        CLI_test.go:27: nao armazenou o vencedor correto, recebi 'Chris' esperava 'Cleo'
FAIL
```

## Escreva código suficiente para fazer ele passar

Vamos usar o [`bufio.Scanner`](https://golang.org/pkg/bufio/) para ler o que foi digitado no `io.Reader`.

> O pacote bufio implementa \[buffered]\(<https://pt.wikipedia.org/wiki/Buffer_(ci%C3%AAncia_da_computa%C3%A7%C3%A3o>)) [I/O](https://pt.wikipedia.org/wiki/Entrada/sa%C3%ADda). Ele encapsula um objeto io.Reader ou io.Writer, criando um outro objeto (Reader ou Writer) que também implementa a interface mas prover buffering e ajuda com entradas/saídas de textos.

Atualize o código para

```go
type CLI struct {
    armazenamentoJogador ArmazenamentoJogador
    in          io.Reader
}

func (cli *CLI) JogarPoquer() {
    reader := bufio.NewScanner(cli.in)
    reader.Scan()
    cli.armazenamentoJogador.GravarVitoria(extrairVencedor(reader.Text()))
}

func extrairVencedor(userInput string) string {
    return strings.Replace(userInput, " venceu", "", 1)
}
```

O teste agora vai passar.

* `Scanner.Scan()` vai ler até o carácter de nova linha.
* Só então usamos `Scanner.Text()` para returnar a `string` lida pelo scanner.

Agora que temos alguns testes passando, devemos amarrar isso ao nosso `main`. Lembre-se que devemos sempre almejar ter o código funcionando totalmente integrado o mais rápido que pudermos.

No `main.go` adicione o seguinte e execute. (você pode ter que ajustar o caminho da segunda dependência para refletir o que tem no seu computador)

```go
package main

import (
    "fmt"
    "github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v3"
    "log"
    "os"
)

const nomeArquivoBD = "jogo.db.json"

func main() {
    fmt.Println("Vamos jogar poquer")
    fmt.Println("Digite {Nome} venceu para registrar uma vitoria")

    db, err := os.OpenFile(nomeArquivoBD, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        log.Fatalf("falha ao abrir %s %v", nomeArquivoBD, err)
    }

    armazenamento, err := poquer.NovoArmazenamentoSistemaDeArquivodeJogador(db)

    if err != nil {
        log.Fatalf("falha ao criar sistema de arquivos para armazenar jogadores, %v ", err)
    }

    jogo := poquer.CLI{armazenamento, os.Stdin}
    jogo.JogarPoquer()
}
```

Você deve receber um erro:

```
linha-de-comando/v3/cmd/cli/main.go:32:25: implicit assignment of unexported field 'armazenamentoJogador' in poquer.CLI literal
linha-de-comando/v3/cmd/cli/main.go:32:34: implicit assignment of unexported field 'in' in poquer.CLI literal
```

O que está acontecendo é que por causa da tentativa de associar os campos `armazenamentoJogador` e `in` na `CLI`. Eles são campos não exportados(privados). Nós *podemos* fazer isso nos nossos testes porque o teste está no mesmo pacote da `CLI` (`poquer`). Mas nosso `main` é um pacote `main` portanto não tem acesso.

Isso enfatiza a importância de *integrar seu código*. Nós definimos corretamente as dependências da `CLI` como privada (porque não queremos expô-las para os usuários da `CLI`) mas não criamos uma forma para os usuário construí-las.

Existe alguma forma de identificarmos esse problema antes?

### `package mypackage_test`

Nos exemplos usados até agora, quando nós fazemos um arquivo para testes nós declaramos ele como pertencendo ao mesmo pacote que estamos testando.

Tudo bem e fazer isso significa no pior dos casos que queremos testar algo que é pertecente somente aquele pacote conseguimos acesso aos tipos não exportados.

Mas considerando que, *em geral*, advogamos para *não* se fazer testes de coisas internas, como Go pode garantir isso? E se pudéssemos testar nosso código aonde somente temos acesso aos tipos exportados (como em nossp `main`)?

Quando você escreve um project com múltiplos pacotes eu recomendo fortmente que o nome to seu pacote tenha o sufixo `_test`. Fazendo isso você somente ter acesso aos tipos públicos no seu pacote. Isso ajuda nesse caso especificamente mas também ajuda a disciplinar o teste somente de APIs públicas. Se ainda assim você precisar testar coisa interna você pode criar um teste separado com o nome de pacote igual ao do que você quer testar.

A máxima do TDD é que se você não pode testar o seu código então provávelmente vai ser difícil para os usuários do seu código de integrar com ele. Fazendo uso de `package foo_test` vai forçar você à testar seu código como se você estivesse importando ele como vão fazer aqueles que importarem o seu pacote.

Antes de consertar o `main` vamos mudar o nome de pacote do nosso teste dentro de `CLI_test.go` para `poquer_test`.

Se sua IDE estiver bem configurada você vai de repente ver um monte de vermelho! Se você rodar o compilador vocês vai ver os seguintes errors:

```
./CLI_test.go:12:19: undefined: EsbocoArmazenamentoJogador
./CLI_test.go:17:3: undefined: verificaVitoriaJogador
./CLI_test.go:22:19: undefined: EsbocoArmazenamentoJogador
./CLI_test.go:27:3: undefined: verificaVitoriaJogador
```

Nós agora tropeçamenos nos problemas de desenho do pacote. Para testar nosso código nós criamos algumas funções auxíliares e tipos emulados sem exportá-los e portanto não estão mais disponíveis para uso no nosso `CLI_test` porque eles foram definidos somente nos arquivos com `_test.go` no pacote `poquer`.

#### Queremos ter as funções auxíliares e tipos emulados disponível publicamente?

Está é uma discussão subjetiva. One argumento é que não queremos poluir a API do nosso pacote só para ter código que facilitam os tests.

Na apresentação ["Testes avançados em Go"](https://speakerdeck.com/mitchellh/advanced-testing-with-go?slide=53) do Mitchell Hashimoto, é descrito como eles advogam na HashiCorp isso para que usuários do pacote possam escrever testes sem ter que reinventar a roda escrevendo tipos emulados. No nosso caso, isso significa que qualquer um usando nosso pacote `poquer` não tem que criar seus próprios `ArmazenamentoJogador` emulados se eles quiserem usar nosso código.

Informalmente eu tenho usado esta técnica em outros pacotes compartilhados e tem se provado extremamente útil em termos de economizar tempo dos usuários quando eles integram com nossos pacotes.

Então vamos criar um arquivo chamado `testing.go` e adicionar nossos cógidos auxiliares nele.

```go
package poquer

import "testing"

type EsbocoArmazenamentoJogador struct {
    pontuacoes   map[string]int
    chamadasDeVitoria []string
    liga   []Jogador
}

func (s *EsbocoArmazenamentoJogador) ObterPontuacaoDeJogador(nome string) int {
    pontuacao := s.pontuacoes[nome]
    return pontuacao
}

func (s *EsbocoArmazenamentoJogador) GravarVitoria(nome string) {
    s.chamadasDeVitoria = append(s.chamadasDeVitoria, nome)
}

func (s *EsbocoArmazenamentoJogador) ObterLiga() Liga {
    return s.liga
}

func VerificaVitoriaJogador(t *testing.T, armazenamento *EsbocoArmazenamentoJogador, vencedor string) {
    t.Helper()

    if len(armazenamento.ChamadasDeVitoria) != 1 {
        t.Fatalf("recebi %d chamadas de GravarVitoria esperava %d", len(armazenamento.ChamadasDeVitoria), 1)
    }

    if armazenamento.ChamadasDeVitoria[0] != vencedor {
        t.Errorf("nao armazenou o vencedor correto, recebi '%s' esperava '%s'", armazenamento.ChamadasDeVitoria[0], vencedor)
    }
}

// tarefa para você - adicionar os códigos restantes
```

Você precisar tornar essas funções públicas (lembre-se que exportar em Go é feito apenas colocando a primeira letra em maíusculo) se você quiser que elas sejam expostas para quem importar esse pacote.

No nosso teste `CLI` você precisa chamar o código como se fosse usando de um pacote diferente.

```go
func TestCLI(t *testing.T) {

    t.Run("recorda vencedor chris digitado pelo usuario", func(t *testing.T) {
        in := strings.NewReader("Chris venceu\n")
        armazenamentoJogador := &poquer.EsbocoArmazenamentoJogador{}

        cli := &poquer.CLI{armazenamentoJogador, in}
        cli.JogarPoquer()

        poquer.VerificaVitoriaJogador(t, armazenamentoJogador, "Chris")
    })

    t.Run("recorda vencedor cleo digitado pelo usuario", func(t *testing.T) {
        in := strings.NewReader("Cleo venceu\n")
        armazenamentoJogador := &poquer.EsbocoArmazenamentoJogador{}

        cli := &poquer.CLI{armazenamentoJogador, in}
        cli.JogarPoquer()

        poquer.VerificaVitoriaJogador(t, armazenamentoJogador, "Cleo")
    })

}
```

Você vai ver que agora temos o mesmo problema que tivemos na `main`

```
./CLI_test.go:15:26: implicit assignment of unexported field 'armazenamentoJogador' in poquer.CLI literal
./CLI_test.go:15:39: implicit assignment of unexported field 'in' in poquer.CLI literal
./CLI_test.go:25:26: implicit assignment of unexported field 'armazenamentoJogador' in poquer.CLI literal
./CLI_test.go:25:39: implicit assignment of unexported field 'in' in poquer.CLI literal
```

O jeito mais fácil de resolver isso é fazer um construtor como temos para outros tipos. Nós também vamos mudar o `CLI` para que ele armazene a `bufio.Scanner` ao invés do leitor pois ele vai ser automaticamente encapsulado no momento da construção.

```go
type CLI struct {
    armazenamentoJogador ArmazenamentoJogador
    in          *bufio.Scanner
}

func NovoCLI(armazenamento ArmazenamentoJogador, in io.Reader) *CLI {
    return &CLI{
        armazenamentoJogador: armazenamento,
        in:          bufio.NewScanner(in),
    }
}
```

Fazendo isso, podemos simplificar e refatorar no código do leitor

```go
func (cli *CLI) JogarPoquer() {
    userInput := cli.readLine()
    cli.armazenamentoJogador.GravarVitoria(extrairVencedor(userInput))
}

func extrairVencedor(userInput string) string {
    return strings.Replace(userInput, " venceu", "", 1)
}

func (cli *CLI) readLine() string {
    cli.in.Scan()
    return cli.in.Text()
}
```

Mude o teste para usar o esse construtor e valtamos a ter nossos testes passando.

Por último, podemos voltar para o nosso `main.go` e usar o construtor que acabamos de criar

```go
jogo := poquer.NovoCLI(armazenamento, os.Stdin)
```

Tente executar ele, digite "Bob venceu".

### Refatoração

Nós temos alguma repetição nas nossas respectivas aplicações aonde estamos abrindo um arquivo e criando um `ArmazenamentoSistemaArquivo` a partir do seu conteúdo. Isso parece uma pequena fraqueza no desenho do nosso pacote então deveríamos fazer uma função nele para encapsular a abertura de arquivos dado um caminho e retornar a `ArmazenamentoJogador`.

```go
func ArmazenamentoSistemaDeArquivoJogadorAPartirDeArquivo(path string) (*SistemaDeArquivoArmazenamentoJogador, func(), error) {
    db, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        return nil, nil, fmt.Errorf("falha ao abrir %s %v", path, err)
    }

    closeFunc := func() {
        db.Close()
    }

    armazenamento, err := NovoArmazenamentoSistemaDeArquivodeJogador(db)

    if err != nil {
        return nil, nil, fmt.Errorf("falha ao criar sistema de arquivos para armazenar jogadores, %v ", err)
    }

    return armazenamento, closeFunc, nil
}
```

Agora refatorando ambas aplicações para usar a função de criar o armazenamento.

#### Código da aplicação CLI

```go
package main

import (
    "github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v3"
    "log"
    "os"
    "fmt"
)

const nomeArquivoBD = "jogo.db.json"

func main() {
    armazenamento, close, err := poquer.ArmazenamentoSistemaDeArquivoJogadorAPartirDeArquivo(nomeArquivoBD)

    if err != nil {
        log.Fatal(err)
    }
    defer close()

    fmt.Println("Vamos jogar poquer")
    fmt.Println("Digite {Nome} venceu para registrar uma vitoria")
    poquer.NovoCLI(armazenamento, os.Stdin).JogarPoquer()
}
```

#### Código da aplicação do servidor Web

```go
package main

import (
    "github.com/larien/aprenda-go-com-testes/criando-uma-aplicacao/linha-de-comando/v3"
    "log"
    "net/http"
)

const nomeArquivoBD = "jogo.db.json"

func main() {
    armazenamento, close, err := poquer.ArmazenamentoSistemaDeArquivoJogadorAPartirDeArquivo(nomeArquivoBD)

    if err != nil {
        log.Fatal(err)
    }
    defer close()

    servidor := poquer.NovoServidorJogador(armazenamento)

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("nao foi possivel escutar na porta 5000 %v", err)
    }
}
```

Note a simetria: mesmo sendo diferente interfaces de usuário o setup é quase idêntico. Isso dá impressão de uma boa validação do nosso desenho. E note também que `ArmazenamentoSistemaDeArquivoJogadorAPartirDeArquivo` retorna uma função `close` (fechar), que podemos encerrar o arquivo fundamental assim que terminarmos de usar o armazenamento.

## Resumindo

### Estrutura do pacote

Esse capítulo pretendia criar duas aplicações, reusar o código de domínio que escrevemos até agora. Para fazer isso, nós precisamos atualizar a estrutura do nosso pacote para que ela tivesse pastas separadas para nossos respectivos `main`s.

Fazendo isso nós enfrentamos problemas de integração devido a valores não exportados então demostrando o valor de trabalhar em pequenas "etapas" e integrar com frequência.

Aprendemos como `mypackage_test` ajudou a criar um ambiente de testes que prover a mesma experiência de outros pacotes integrando com nosso código, assim ajudando você a pegar problemas de integração e ver o quão fácil (ou não) é de usar seu código.

### Lendo a entrada do usuário

Vimos como lendo do `os.Stdin` é muito fácil de usar pois ele implementa o `io.Reader`. Nós usamos `bufio.Scanner` para facilitar a leitura linha à linha do que o usuário digita.

### Abstração simples leva à simples reutilização de código

Quase não nos esforçamos para integrar a `ArmazenamentoJogador` na nossa aplicação (assim que fizemos alguns ajustes no pacode) e subsequente testar foi muito fácil tambem porque nós decidimos também expor a versão emulada.


# Tempo

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

The product owner wants us to expand the functionality of our command line application by helping a group of people play Texas-Holdem Poker.

## Just enough information on poker

You wont need to know much about poker, only that at certain time intervals all the players need to be informed of a steadily increasing "blind" value.

Our application will help keep track of when the blind should go up, and how much it should be.

* When it starts it asks how many players are playing. This determines the amount of time there is before the "blind" bet goes up.
  * There is a base amount of time of 5 minutes.
  * For every player, 1 minute is added.
  * e.g 6 players equals 11 minutes for the blind.
* After the blind time expires the game should alert the players the new amount the blind bet is.
* The blind starts at 100 chips, then 200, 400, 600, 1000, 2000 and continue to double until the game ends (our previous functionality of "Ruth wins" should still finish the game)

## Reminder of the code

In the previous chapter we made our start to the command line application which already accepts a command of `{name} wins`. Here is what the current `CLI` code looks like, but be sure to familiarise yourself with the other code too before starting.

```go
type CLI struct {
    playerStore PlayerStore
    in          *bufio.Scanner
}

func NewCLI(store PlayerStore, in io.Reader) *CLI {
    return &CLI{
        playerStore: store,
        in:          bufio.NewScanner(in),
    }
}

func (cli *CLI) PlayPoker() {
    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}

func extractWinner(userInput string) string {
    return strings.Replace(userInput, " wins", "", 1)
}

func (cli *CLI) readLine() string {
    cli.in.Scan()
    return cli.in.Text()
}
```

### `time.AfterFunc`

We want to be able to schedule our program to print the blind bet values at certain durations dependant on the number of players.

To limit the scope of what we need to do, we'll forget about the number of players part for now and just assume there are 5 players so we'll test that *every 10 minutes the new value of the blind bet is printed*.

As usual the standard library has us covered with [`func AfterFunc(d Duration, f func()) *Timer`](https://golang.org/pkg/time/#AfterFunc)

> `AfterFunc` waits for the duration to elapse and then calls f in its own goroutine. It returns a `Timer` that can be used to cancel the call using its Stop method.

### [`time.Duration`](https://golang.org/pkg/time/#Duration)

> A Duration represents the elapsed time between two instants as an int64 nanosecond count.

The time library has a number of constants to let you multiply those nanoseconds so they're a bit more readable for the kind of scenarios we'll be doing

```go
5 * time.Second
```

When we call `PlayPoker` we'll schedule all of our blind alerts.

Testing this may be a little tricky though. We'll want to verify that each time period is scheduled with the correct blind amount but if you look at the signature of `time.AfterFunc` its second argument is the function it will run. You cannot compare functions in Go so we'd be unable to test what function has been sent in. So we'll need to write some kind of wrapper around `time.AfterFunc` which will take the time to run and the amount to print so we can spy on that.

## Write the test first

Add a new test to our suite

```go
t.Run("it schedules printing of blind values", func(t *testing.T) {
    in := strings.NewReader("Chris wins\n")
    playerStore := &poker.StubPlayerStore{}
    blindAlerter := &SpyBlindAlerter{}

    cli := poker.NewCLI(playerStore, in, blindAlerter)
    cli.PlayPoker()

    if len(blindAlerter.alerts) != 1 {
        t.Fatal("expected a blind alert to be scheduled")
    }
})
```

You'll notice we've made a `SpyBlindAlerter` which we are trying to inject into our `CLI` and then checking that after we call `PlayerPoker` that an alert is scheduled.

(Remember we are just going for the simplest scenario first and then we'll iterate.)

Here's the definition of `SpyBlindAlerter`

```go
type SpyBlindAlerter struct {
    alerts []struct{
        scheduledAt time.Duration
        amount int
    }
}

func (s *SpyBlindAlerter) ScheduleAlertAt(duration time.Duration, amount int) {
    s.alerts = append(s.alerts, struct {
        scheduledAt time.Duration
        amount int
    }{duration,  amount})
}
```

## Try to run the test

```
./CLI_test.go:32:27: too many arguments in call to poker.NewCLI
    have (*poker.StubPlayerStore, *strings.Reader, *SpyBlindAlerter)
    want (poker.PlayerStore, io.Reader)
```

## Write the minimal amount of code for the test to run and check the failing test output

We have added a new argument and the compiler is complaining. *Strictly speaking* the minimal amount of code is to make `NewCLI` accept a `*SpyBlindAlerter` but let's cheat a little and just define the dependency as an interface.

```go
type BlindAlerter interface {
    ScheduleAlertAt(duration time.Duration, amount int)
}
```

And then add it to the constructor

```go
func NewCLI(store PlayerStore, in io.Reader, alerter BlindAlerter) *CLI
```

Your other tests will now fail as they dont have a `BlindAlerter` passed in to `NewCLI`.

Spying on BlindAlerter is not relevant for the other tests so in the test file add

```go
var dummySpyAlerter = &SpyBlindAlerter{}
```

Then use that in the other tests to fix the compilation problems. By labelling it as a "dummy" it is clear to the reader of the test that it is not important.

[> Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.](https://martinfowler.com/articles/mocksArentStubs.html)

The tests should now compile and our new test fails.

```
=== RUN   TestCLI
=== RUN   TestCLI/it_schedules_printing_of_blind_values
--- FAIL: TestCLI (0.00s)
    --- FAIL: TestCLI/it_schedules_printing_of_blind_values (0.00s)
        CLI_test.go:38: expected a blind alert to be scheduled
```

## Write enough code to make it pass

We'll need to add the `BlindAlerter` as a field on our `CLI` so we can reference it in our `PlayPoker` method.

```go
type CLI struct {
    playerStore PlayerStore
    in          *bufio.Reader
    alerter     BlindAlerter
}

func NewCLI(store PlayerStore, in io.Reader, alerter BlindAlerter) *CLI {
    return &CLI{
        playerStore: store,
        in:          bufio.NewReader(in),
        alerter:     alerter,
    }
}
```

To make the test pass, we can call our `BlindAlerter` with anything we like

```go
func (cli *CLI) PlayPoker() {
    cli.alerter.ScheduleAlertAt(5 * time.Second, 100)
    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}
```

Next we'll want to check it schedules all the alerts we'd hope for, for 5 players

## Write the test first

```go
    t.Run("it schedules printing of blind values", func(t *testing.T) {
        in := strings.NewReader("Chris wins\n")
        playerStore := &poker.StubPlayerStore{}
        blindAlerter := &SpyBlindAlerter{}

        cli := poker.NewCLI(playerStore, in, blindAlerter)
        cli.PlayPoker()

        cases := []struct{
            expectedScheduleTime time.Duration
            expectedAmount       int
        } {
            {0 * time.Second, 100},
            {10 * time.Minute, 200},
            {20 * time.Minute, 300},
            {30 * time.Minute, 400},
            {40 * time.Minute, 500},
            {50 * time.Minute, 600},
            {60 * time.Minute, 800},
            {70 * time.Minute, 1000},
            {80 * time.Minute, 2000},
            {90 * time.Minute, 4000},
            {100 * time.Minute, 8000},
        }

        for i, c := range cases {
            t.Run(fmt.Sprintf("%d scheduled for %v", c.expectedAmount, c.expectedScheduleTime), func(t *testing.T) {

                if len(blindAlerter.alerts) <= i {
                    t.Fatalf("alert %d was not scheduled %v", i, blindAlerter.alerts)
                }

                alert := blindAlerter.alerts[i]

                amountGot := alert.amount
                if amountGot != c.expectedAmount {
                    t.Errorf("got amount %d, want %d", amountGot, c.expectedAmount)
                }

                gotScheduledTime := alert.scheduledAt
                if gotScheduledTime != c.expectedScheduleTime {
                    t.Errorf("got scheduled time of %v, want %v", gotScheduledTime, c.expectedScheduleTime)
                }
            })
        }
    })
```

Table-based test works nicely here and clearly illustrate what our requirements are. We run through the table and check the `SpyBlindAlerter` to see if the alert has been scheduled with the correct values.

## Try to run the test

You should have a lot of failures looking like this

```go
=== RUN   TestCLI
--- FAIL: TestCLI (0.00s)
=== RUN   TestCLI/it_schedules_printing_of_blind_values
    --- FAIL: TestCLI/it_schedules_printing_of_blind_values (0.00s)
=== RUN   TestCLI/it_schedules_printing_of_blind_values/100_scheduled_for_0s
        --- FAIL: TestCLI/it_schedules_printing_of_blind_values/100_scheduled_for_0s (0.00s)
            CLI_test.go:71: got scheduled time of 5s, want 0s
=== RUN   TestCLI/it_schedules_printing_of_blind_values/200_scheduled_for_10m0s
        --- FAIL: TestCLI/it_schedules_printing_of_blind_values/200_scheduled_for_10m0s (0.00s)
            CLI_test.go:59: alert 1 was not scheduled [{5000000000 100}]
```

## Write enough code to make it pass

```go
func (cli *CLI) PlayPoker() {

    blinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}
    blindTime := 0 * time.Second
    for _, blind := range blinds {
        cli.alerter.ScheduleAlertAt(blindTime, blind)
        blindTime = blindTime + 10 * time.Minute
    }

    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}
```

It's not a lot more complicated than what we already had. We're just now iterating over an array of `blinds` and calling the scheduler on an increasing `blindTime`

## Refactor

We can encapsulate our scheduled alerts into a method just to make `PlayPoker` read a little clearer.

```go
func (cli *CLI) PlayPoker() {
    cli.scheduleBlindAlerts()
    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}

func (cli *CLI) scheduleBlindAlerts() {
    blinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}
    blindTime := 0 * time.Second
    for _, blind := range blinds {
        cli.alerter.ScheduleAlertAt(blindTime, blind)
        blindTime = blindTime + 10*time.Minute
    }
}
```

Finally our tests are looking a little clunky. We have two anonymous structs representing the same thing, a `ScheduledAlert`. Let's refactor that into a new type and then make some helpers to compare them.

```go
type scheduledAlert struct {
    at time.Duration
    amount int
}

func (s scheduledAlert) String() string {
    return fmt.Sprintf("%d chips at %v", s.amount, s.at)
}

type SpyBlindAlerter struct {
    alerts []scheduledAlert
}

func (s *SpyBlindAlerter) ScheduleAlertAt(at time.Duration, amount int) {
    s.alerts = append(s.alerts, scheduledAlert{at, amount})
}
```

We've added a `String()` method to our type so it prints nicely if the test fails

Update our test to use our new type

```go
t.Run("it schedules printing of blind values", func(t *testing.T) {
    in := strings.NewReader("Chris wins\n")
    playerStore := &poker.StubPlayerStore{}
    blindAlerter := &SpyBlindAlerter{}

    cli := poker.NewCLI(playerStore, in, blindAlerter)
    cli.PlayPoker()

    cases := []scheduledAlert {
        {0 * time.Second, 100},
        {10 * time.Minute, 200},
        {20 * time.Minute, 300},
        {30 * time.Minute, 400},
        {40 * time.Minute, 500},
        {50 * time.Minute, 600},
        {60 * time.Minute, 800},
        {70 * time.Minute, 1000},
        {80 * time.Minute, 2000},
        {90 * time.Minute, 4000},
        {100 * time.Minute, 8000},
    }

    for i, want := range cases {
        t.Run(fmt.Sprint(want), func(t *testing.T) {

            if len(blindAlerter.alerts) <= i {
                t.Fatalf("alert %d was not scheduled %v", i, blindAlerter.alerts)
            }

            got := blindAlerter.alerts[i]
            assertScheduledAlert(t, got, want)
        })
    }
})
```

Implement `assertScheduledAlert` yourself.

We've spent a fair amount of time here writing tests and have been somewhat naughty not integrating with our application. Let's address that before we pile on any more requirements.

Try running the app and it wont compile, complaining about not enough args to `NewCLI`.

Let's create an implementation of `BlindAlerter` that we can use in our application.

Create `BlindAlerter.go` and move our `BlindAlerter` interface and add the new things below

```go
package poker

import (
    "time"
    "fmt"
    "os"
)

type BlindAlerter interface {
    ScheduleAlertAt(duration time.Duration, amount int)
}

type BlindAlerterFunc func(duration time.Duration, amount int)

func (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {
    a(duration, amount)
}

func StdOutAlerter(duration time.Duration, amount int) {
    time.AfterFunc(duration, func() {
        fmt.Fprintf(os.Stdout, "Blind is now %d\n", amount)
    })
}
```

Remember that any *type* can implement an interface, not just `structs`. If you are making a library that exposes an interface with one function defined it is a common idiom to also expose a `MyInterfaceFunc` type.

This type will be a `func` which will also implement your interface. That way users of your interface have the option to implement your interface with just a function; rather than having to create an empty `struct` type.

We then create the function `StdOutAlerter` which has the same signature as the function and just use `time.AfterFunc` to schedule it to print to `os.Stdout`.

Update `main` where we create `NewCLI` to see this in action

```go
poker.NewCLI(store, os.Stdin, poker.BlindAlerterFunc(poker.StdOutAlerter)).PlayPoker()
```

Before running you might want to change the `blindTime` increment in `CLI` to be 10 seconds rather than 10 minutes just so you can see it in action.

You should see it print the blind values as we'd expect every 10 seconds. Notice how you can still type `Shaun wins` into the CLI and it will stop the program how we'd expect.

The game wont always be played with 5 people so we need to prompt the user to enter a number of players before the game starts.

## Write the test first

To check we are prompting for the number of players we'll want to record what is written to StdOut. We've done this a few times now, we know that `os.Stdout` is an `io.Writer` so we can check what is written if we use dependency injection to pass in a `bytes.Buffer` in our test and see what our code will write.

We don't care about our other collaborators in this test just yet so we've made some dummies in our test file.

We should be a little wary that we now have 4 dependencies for `CLI`, that feels like maybe it is starting to have too many responsibilities. Let's live with it for now and see if a refactoring emerges as we add this new functionality.

```go
var dummyBlindAlerter = &SpyBlindAlerter{}
var dummyPlayerStore = &poker.StubPlayerStore{}
var dummyStdIn = &bytes.Buffer{}
var dummyStdOut = &bytes.Buffer{}
```

Here is our new test

```go
t.Run("it prompts the user to enter the number of players", func(t *testing.T) {
    stdout := &bytes.Buffer{}
    cli := poker.NewCLI(dummyPlayerStore, dummyStdIn, stdout, dummyBlindAlerter)
    cli.PlayPoker()

    got := stdout.String()
    want := "Please enter the number of players: "

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }
})
```

We pass in what will be `os.Stdout` in `main` and see what is written.

## Try to run the test

```
./CLI_test.go:38:27: too many arguments in call to poker.NewCLI
    have (*poker.StubPlayerStore, *bytes.Buffer, *bytes.Buffer, *SpyBlindAlerter)
    want (poker.PlayerStore, io.Reader, poker.BlindAlerter)
```

## Write the minimal amount of code for the test to run and check the failing test output

We have a new dependency so we'll have to update `NewCLI`

```go
func NewCLI(store PlayerStore, in io.Reader, out io.Writer, alerter BlindAlerter) *CLI
```

Now the *other* tests will fail to compile because they dont have an `io.Writer` being passed into `NewCLI`.

Add `dummyStdout` for the other tests.

The new test should fail like so

```
=== RUN   TestCLI
--- FAIL: TestCLI (0.00s)
=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players
    --- FAIL: TestCLI/it_prompts_the_user_to_enter_the_number_of_players (0.00s)
        CLI_test.go:46: got '', want 'Please enter the number of players: '
FAIL
```

## Write enough code to make it pass

We need to add our new dependency to our `CLI` so we can reference it in `PlayPoker`

```go
type CLI struct {
    playerStore PlayerStore
    in          *bufio.Reader
    out         io.Writer
    alerter     BlindAlerter
}

func NewCLI(store PlayerStore, in io.Reader, out io.Writer, alerter BlindAlerter) *CLI {
    return &CLI{
        playerStore: store,
        in:          bufio.NewReader(in),
        out:         out,
        alerter:     alerter,
    }
}
```

Then finally we can write our prompt at the start of the game

```go
func (cli *CLI) PlayPoker() {
    fmt.Fprint(cli.out, "Please enter the number of players: ")
    cli.scheduleBlindAlerts()
    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}
```

## Refactor

We have a duplicate string for the prompt which we should extract into a constant

```go
const PlayerPrompt = "Please enter the number of players: "
```

Use this in both the test code and `CLI`.

Now we need to send in a number and extract it out. The only way we'll know if it has had the desired effect is by seeing what blind alerts were scheduled.

## Write the test first

```go
t.Run("it prompts the user to enter the number of players", func(t *testing.T) {
    stdout := &bytes.Buffer{}
    in := strings.NewReader("7\n")
    blindAlerter := &SpyBlindAlerter{}

    cli := poker.NewCLI(dummyPlayerStore, in, stdout, blindAlerter)
    cli.PlayPoker()

    got :=stdout.String()
    want := poker.PlayerPrompt

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }

    cases := []scheduledAlert{
        {0 * time.Second, 100},
        {12 * time.Minute, 200},
        {24 * time.Minute, 300},
        {36 * time.Minute, 400},
    }

    for i, want := range cases {
        t.Run(fmt.Sprint(want), func(t *testing.T) {

            if len(blindAlerter.alerts) <= i {
                t.Fatalf("alert %d was not scheduled %v", i, blindAlerter.alerts)
            }

            got := blindAlerter.alerts[i]
            assertScheduledAlert(t, got, want)
        })
    }
})
```

Ouch! A lot of changes.

* We remove our dummy for StdIn and instead send in a mocked version representing our user entering 7
* We also remove our dummy on the blind alerter so we can see that the number of players has had an effect on the scheduling
* We test what alerts are scheduled

## Try to run the test

The test should still compile and fail reporting that the scheduled times are wrong because we've hard-coded for the game to be based on having 5 players

```
=== RUN   TestCLI
--- FAIL: TestCLI (0.00s)
=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players
    --- FAIL: TestCLI/it_prompts_the_user_to_enter_the_number_of_players (0.00s)
=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players/100_chips_at_0s
        --- PASS: TestCLI/it_prompts_the_user_to_enter_the_number_of_players/100_chips_at_0s (0.00s)
=== RUN   TestCLI/it_prompts_the_user_to_enter_the_number_of_players/200_chips_at_12m0s
```

## Write enough code to make it pass

Remember, we are free to commit whatever sins we need to make this work. Once we have working software we can then work on refactoring the mess we're about to make!

```go
func (cli *CLI) PlayPoker() {
    fmt.Fprint(cli.out, PlayerPrompt)

    numberOfPlayers, _ := strconv.Atoi(cli.readLine())

    cli.scheduleBlindAlerts(numberOfPlayers)

    userInput := cli.readLine()
    cli.playerStore.RecordWin(extractWinner(userInput))
}

func (cli *CLI) scheduleBlindAlerts(numberOfPlayers int) {
    blindIncrement := time.Duration(5 + numberOfPlayers) * time.Minute

    blinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}
    blindTime := 0 * time.Second
    for _, blind := range blinds {
        cli.alerter.ScheduleAlertAt(blindTime, blind)
        blindTime = blindTime + blindIncrement
    }
}
```

* We read in the `numberOfPlayersInput` into a string
* We use `cli.readLine()` to get the input from the user and then call `Atoi` to convert it into an integer - ignoring any error scenarios. We'll need to write a test for that scenario later.
* From here we change `scheduleBlindAlerts` to accept a number of players. We then calculate a `blindIncrement` time to use to add to `blindTime` as we iterate over the blind amounts

While our new test has been fixed, a lot of others have failed because now our system only works if the game starts with a user entering a number. You'll need to fix the tests by changing the user inputs so that a number followed by a newline is added (this is highlighting yet more flaws in our approach right now).

## Refactor

This all feels a bit horrible right? Let's **listen to our tests**.

* In order to test that we are scheduling some alerts we set up 4 different dependencies. Whenever you have a lot of dependencies for a *thing* in your system, it implies it's doing too much. Visually we can see it in how cluttered our test is.
* To me it feels like **we need to make a cleaner abstraction between reading user input and the business logic we want to do**&#x20;
* A better test would be *given this user input, do we call a new type `Game` with the correct number of players*.&#x20;
* We would then extract the testing of the scheduling into the tests for our new `Game`.

We can refactor toward our `Game` first and our test should continue to pass. Once we've made the structural changes we want we can think about how we can refactor the tests to reflect our new separation of concerns

Remember when making changes in refactoring try to keep them as small as possible and keep re-running the tests.

Try it yourself first. Think about the boundaries of what a `Game` would offer and what our `CLI` should be doing.

For now **don't** change the external interface of `NewCLI` as we don't want to change the test code and the client code at the same time as that is too much to juggle and we could end up breaking things.

This is what I came up with:

```go
// game.go
type Game struct {
    alerter BlindAlerter
    store   PlayerStore
}

func (p *Game) Start(numberOfPlayers int) {
    blindIncrement := time.Duration(5+numberOfPlayers) * time.Minute

    blinds := []int{100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, 8000}
    blindTime := 0 * time.Second
    for _, blind := range blinds {
        p.alerter.ScheduleAlertAt(blindTime, blind)
        blindTime = blindTime + blindIncrement
    }
}

func (p *Game) Finish(winner string) {
    p.store.RecordWin(winner)
}

// cli.go
type CLI struct {
    playerStore PlayerStore
    in          *bufio.Reader
    out         io.Writer
    game        *Game
}

func NewCLI(store PlayerStore, in io.Reader, out io.Writer, alerter BlindAlerter) *CLI {
    return &CLI{
        in:  bufio.NewReader(in),
        out: out,
        game: &Game{
            alerter: alerter,
            store:   store,
        },
    }
}

const PlayerPrompt = "Please enter the number of players: "

func (cli *CLI) PlayPoker() {
    fmt.Fprint(cli.out, PlayerPrompt)

    numberOfPlayersInput := cli.readLine()
    numberOfPlayers, _ := strconv.Atoi(strings.Trim(numberOfPlayersInput, "\n"))

    cli.game.Start(numberOfPlayers)

    winnerInput := cli.readLine()
    winner := extractWinner(winnerInput)

    cli.game.Finish(winner)
}

func extractWinner(userInput string) string {
    return strings.Replace(userInput, " wins\n", "", 1)
}

func (cli *CLI) readLine() string {
    cli.in.Scan()
    return cli.in.Text()
}
```

From a "domain" perspective:

* We want to `Start` a `Game`, indicating how many people are playing
* We want to `Finish` a `Game`, declaring the winner

The new `Game` type encapsulates this for us.

With this change we've passed `BlindAlerter` and `PlayerStore` to `Game` as it is now responsible for alerting and storing results.

Our `CLI` is now just concerned with:

* Constructing `Game` with its existing dependencies (which we'll refactor next)
* Interpreting user input as method invocations for `Game`

We want to try to avoid doing "big" refactors which leave us in a state of failing tests for extended periods as that increases the chances of mistakes. (If you are working in a large/distributed team this is extra important)

The first thing we'll do is refactor `Game` so that we inject it into `CLI`. We'll do the smallest changes in our tests to facilitate that and then we'll see how we can break up the tests into the themes of parsing user input and game management.

All we need to do right now is change `NewCLI`

```go
func NewCLI(in io.Reader, out io.Writer, game *Game) *CLI {
    return &CLI{
        in:  bufio.NewReader(in),
        out: out,
        game: game,
    }
}
```

This feels like an improvement already. We have less dependencies and *our dependency list is reflecting our overall design goal* of CLI being concerned with input/output and delegating game specific actions to a `Game`.

If you try and compile there are problems. You should be able to fix these problems yourself. Don't worry about making any mocks for `Game` right now, just initialise *real* `Game`s just to get everything compiling and tests green.

To do this you'll need to make a constructor

```go
func NewGame(alerter BlindAlerter, store PlayerStore) *Game {
    return &Game{
        alerter:alerter,
        store:store,
    }
}
```

Here's an example of one of the setups for the tests being fixed

```go
stdout := &bytes.Buffer{}
in := strings.NewReader("7\n")
blindAlerter := &SpyBlindAlerter{}
game := poker.NewGame(blindAlerter, dummyPlayerStore)

cli := poker.NewCLI(in, stdout, game)
cli.PlayPoker()
```

It shouldn't take much effort to fix the tests and be back to green again (that's the point!) but make sure you fix `main.go` too before the next stage.

```go
// main.go
game := poker.NewGame(poker.BlindAlerterFunc(poker.StdOutAlerter), store)
cli := poker.NewCLI(os.Stdin, os.Stdout, game)
cli.PlayPoker()
```

Now that we have extracted out `Game` we should move our game specific assertions into tests separate from CLI.

This is just an exercise in copying our `CLI` tests but with less dependencies

```go
func TestGame_Start(t *testing.T) {
    t.Run("schedules alerts on game start for 5 players", func(t *testing.T) {
        blindAlerter := &poker.SpyBlindAlerter{}
        game := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)

        game.Start(5)

        cases := []poker.ScheduledAlert{
            {At: 0 * time.Second, Amount: 100},
            {At: 10 * time.Minute, Amount: 200},
            {At: 20 * time.Minute, Amount: 300},
            {At: 30 * time.Minute, Amount: 400},
            {At: 40 * time.Minute, Amount: 500},
            {At: 50 * time.Minute, Amount: 600},
            {At: 60 * time.Minute, Amount: 800},
            {At: 70 * time.Minute, Amount: 1000},
            {At: 80 * time.Minute, Amount: 2000},
            {At: 90 * time.Minute, Amount: 4000},
            {At: 100 * time.Minute, Amount: 8000},
        }

        checkSchedulingCases(cases, t, blindAlerter)
    })

    t.Run("schedules alerts on game start for 7 players", func(t *testing.T) {
        blindAlerter := &poker.SpyBlindAlerter{}
        game := poker.NewTexasHoldem(blindAlerter, dummyPlayerStore)

        game.Start(7)

        cases := []poker.ScheduledAlert{
            {At: 0 * time.Second, Amount: 100},
            {At: 12 * time.Minute, Amount: 200},
            {At: 24 * time.Minute, Amount: 300},
            {At: 36 * time.Minute, Amount: 400},
        }

        checkSchedulingCases(cases, t, blindAlerter)
    })

}

func TestGame_Finish(t *testing.T) {
    store := &poker.StubPlayerStore{}
    game := poker.NewTexasHoldem(dummyBlindAlerter, store)
    winner := "Ruth"

    game.Finish(winner)
    poker.AssertPlayerWin(t, store, winner)
}
```

The intent behind what happens when a game of poker starts is now much clearer.

Make sure to also move over the test for when the game ends.

Once we are happy we have moved the tests over for game logic we can simplify our CLI tests so they reflect our intended responsibilities clearer

* Process user input and call `Game`'s methods when appropriate
* Send output
* Crucially it doesn't know about the actual workings of how games work

To do this we'll have to make it so `CLI` no longer relies on a concrete `Game` type but instead accepts an interface with `Start(numberOfPlayers)` and `Finish(winner)`. We can then create a spy of that type and verify the correct calls are made.

It's here we realise that naming is awkward sometimes. Rename `Game` to `TexasHoldem` (as that's the *kind* of game we're playing) and the new interface will be called `Game`. This keeps faithful to the notion that our CLI is oblivious to the actual game we're playing and what happens when you `Start` and `Finish`.

```go
type Game interface {
    Start(numberOfPlayers int)
    Finish(winner string)
}
```

Replace all references to `*Game` inside `CLI` and replace them with `Game` (our new interface). As always keep re-running tests to check everything is green while we are refactoring.

Now that we have decoupled `CLI` from `TexasHoldem` we can use spies to check that `Start` and `Finish` are called when we expect them to, with the correct arguments.

Create a spy that implements `Game`

```go
type GameSpy struct {
    StartedWith  int
    FinishedWith string
}

func (g *GameSpy) Start(numberOfPlayers int) {
    g.StartedWith = numberOfPlayers
}

func (g *GameSpy) Finish(winner string) {
    g.FinishedWith = winner
}
```

Replace any `CLI` test which is testing any game specific logic with checks on how our `GameSpy` is called. This will then reflect the responsibilities of CLI in our tests clearly.

Here is an example of one of the tests being fixed; try and do the rest yourself and check the source code if you get stuck.

```go
    t.Run("it prompts the user to enter the number of players and starts the game", func(t *testing.T) {
        stdout := &bytes.Buffer{}
        in := strings.NewReader("7\n")
        game := &GameSpy{}

        cli := poker.NewCLI(in, stdout, game)
        cli.PlayPoker()

        gotPrompt := stdout.String()
        wantPrompt := poker.PlayerPrompt

        if gotPrompt != wantPrompt {
            t.Errorf("got '%s', want '%s'", gotPrompt, wantPrompt)
        }

        if game.StartCalledWith != 7 {
            t.Errorf("wanted Start called with 7 but got %d", game.StartCalledWith)
        }
    })
```

Now that we have a clean separation of concerns, checking edge cases around IO in our `CLI` should be easier.

We need to address the scenario where a user puts a non numeric value when prompted for the number of players:

Our code should not start the game and it should print a handy error to the user and then exit.

## Write the test first

We'll start by making sure the game doesn't start

```go
t.Run("it prints an error when a non numeric value is entered and does not start the game", func(t *testing.T) {
        stdout := &bytes.Buffer{}
        in := strings.NewReader("Pies\n")
        game := &GameSpy{}

        cli := poker.NewCLI(in, stdout, game)
        cli.PlayPoker()

        if game.StartCalled {
            t.Errorf("game should not have started")
        }
    })
```

You'll need to add to our `GameSpy` a field `StartCalled` which only gets set if `Start` is called

## Try to run the test

```
=== RUN   TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game
    --- FAIL: TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game (0.00s)
        CLI_test.go:62: game should not have started
```

## Write enough code to make it pass

Around where we call `Atoi` we just need to check for the error

```go
numberOfPlayers, err := strconv.Atoi(cli.readLine())

if err != nil {
    return
}
```

Next we need to inform the user of what they did wrong so we'll assert on what is printed to `stdout`.

## Write the test first

We've asserted on what was printed to `stdout` before so we can copy that code for now

```go
gotPrompt := stdout.String()

wantPrompt := poker.PlayerPrompt + "you're so silly"

if gotPrompt != wantPrompt {
    t.Errorf("got '%s', want '%s'", gotPrompt, wantPrompt)
}
```

We are storing *everything* that gets written to stdout so we still expect the `poker.PlayerPrompt`. We then just check an additional thing gets printed. We're not too bothered about the exact wording for now, we'll address it when we refactor.

## Try to run the test

```
=== RUN   TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game
    --- FAIL: TestCLI/it_prints_an_error_when_a_non_numeric_value_is_entered_and_does_not_start_the_game (0.00s)
        CLI_test.go:70: got 'Please enter the number of players: ', want 'Please enter the number of players: you're so silly'
```

## Write enough code to make it pass

Change the error handling code

```go
if err != nil {
    fmt.Fprint(cli.out, "you're so silly")
    return
}
```

## Refactor

Now refactor the message into a constant like `PlayerPrompt`

```go
wantPrompt := poker.PlayerPrompt + poker.BadPlayerInputErrMsg
```

and put in a more appropriate message

```go
const BadPlayerInputErrMsg = "Bad value received for number of players, please try again with a number"
```

Finally our testing around what has been sent to `stdout` is quite verbose, let's write an assert function to clean it up.

```go
func assertMessagesSentToUser(t *testing.T, stdout *bytes.Buffer, messages ...string) {
    t.Helper()
    want := strings.Join(messages, "")
    got := stdout.String()
    if got != want {
        t.Errorf("got '%s' sent to stdout but expected %+v", got, messages)
    }
}
```

Using the vararg syntax (`...string`) is handy here because we need to assert on varying amounts of messages.

Use this helper in both of the tests where we assert on messages sent to the user.

There are a number of tests that could be helped with some `assertX` functions so practice your refactoring by cleaning up our tests so they read nicely.

Take some time and think about the value of some of the tests we've driven out. Remember we don't want more tests than necessary, can you refactor/remove some of them *and still be confident it all works* ?

Here is what I came up with

```go
func TestCLI(t *testing.T) {

    t.Run("start game with 3 players and finish game with 'Chris' as winner", func(t *testing.T) {
        game := &GameSpy{}
        stdout := &bytes.Buffer{}

        in := userSends("3", "Chris wins")
        cli := poker.NewCLI(in, stdout, game)

        cli.PlayPoker()

        assertMessagesSentToUser(t, stdout, poker.PlayerPrompt)
        assertGameStartedWith(t, game, 3)
        assertFinishCalledWith(t, game, "Chris")
    })

    t.Run("start game with 8 players and record 'Cleo' as winner", func(t *testing.T) {
        game := &GameSpy{}

        in := userSends("8", "Cleo wins")
        cli := poker.NewCLI(in, dummyStdOut, game)

        cli.PlayPoker()

        assertGameStartedWith(t, game, 8)
        assertFinishCalledWith(t, game, "Cleo")
    })

    t.Run("it prints an error when a non numeric value is entered and does not start the game", func(t *testing.T) {
        game := &GameSpy{}

        stdout := &bytes.Buffer{}
        in := userSends("pies")

        cli := poker.NewCLI(in, stdout, game)
        cli.PlayPoker()

        assertGameNotStarted(t, game)
        assertMessagesSentToUser(t, stdout, poker.PlayerPrompt, poker.BadPlayerInputErrMsg)
    })
}
```

The tests now reflect the main capabilities of CLI, it is able to read user input in terms of how many people are playing and who won and handles when a bad value is entered for number of players. By doing this it is clear to the reader what `CLI` does, but also what it doesn't do.

What happens if instead of putting `Ruth wins` the user puts in `Lloyd is a killer` ?

Finish this chapter by writing a test for this scenario and making it pass.

## Wrapping up

### A quick project recap

For the past 5 chapters we have slowly TDD'd a fair amount of code

* We have two applications, a command line application and a web server.&#x20;
* Both these applications rely on a `PlayerStore` to record winners
* The web server can also display a league table of who is winning the most games
* The command line app helps players play a game of poker by tracking what the current blind value is.

### time.Afterfunc

A very handy way of scheduling a function call after a specific duration. It is well worth investing time [looking at the documentation for `time`](https://golang.org/pkg/time/) as it has a lot of time saving functions and methods for you to work with.

Some of my favourites are

* `time.After(duration)` which return you a `chan Time` when the duration has expired. So if you wish to do something *after* a specific time, this can help.&#x20;
* `time.NewTicker(duration)` returns a `Ticker` which is similar to the above in that it returns a channel but this one "ticks" every duration, rather than just once. This is very handy if you want to execute some code every `N duration`. &#x20;

### More examples of good separation of concerns

*Generally* it is good practice to separate the responsibilities of dealing with user input and responses away from domain code. You see that here in our command line application and also our web server.

Our tests got messy. We had too many assertions (check this input, schedules these alerts, etc) and too many dependencies. We could visually see it was cluttered; it is **so important to listen to your tests**.

* If your tests look messy try and refactor them.
* If you've done this and they're still a mess it is very likely pointing to a flaw in your design
* This is one of the real strengths of tests.

Even though the tests and the production code was a bit cluttered we could freely refactor backed by our tests.

Remember when you get in to these situations to always take small steps and re-run the tests after every change.

It would've been dangerous to refactor both the test code *and* the production code at the same time, so we first refactored the production code (in the current state we couldn't improve the tests much) without changing its interface so we could rely on our tests as much as we could while changing things. *Then* we refactored the tests after the design improved.

After refactoring the dependency list reflected our design goal. This is another benefit of DI in that it often documents intent. When you rely on global variables responsibilities become very unclear.

## An example of a function implementing an interface

When you define an interface with one method in it you might want to consider defining a `MyInterfaceFunc` type to complement it so users can implement your interface with just a function

```go
type BlindAlerter interface {
    ScheduleAlertAt(duration time.Duration, amount int)
}

// BlindAlerterFunc allows you to implement BlindAlerter with a function
type BlindAlerterFunc func(duration time.Duration, amount int)

// ScheduleAlertAt is BlindAlerterFunc implementation of BlindAlerter
func (a BlindAlerterFunc) ScheduleAlertAt(duration time.Duration, amount int) {
    a(duration, amount)
}
```


# Websockets

[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/aprenda-go-com-testes/tree/master/criando-uma-aplicacao/websockets)

## Recapitulando o projeto

Nós temos duas aplicações no nosso código-base de poquer.

* *Aplicação de linha de comando*. Pede ao usuário para que insira o número de jogadores. A partir daí informa os jogadores o valor da "aposta cega", que aumenta em função do tempo. A qualquer momento, um usuário pode entrar com `"{Jogador} ganhou"` para encerrar o jogo e salvar a vitória em um armazenamento.
* *Aplicação Web*. Permite que os usuários salvem os ganhadores e mostrem uma tabela da liga. Divide o armazenamento com a aplicação de linha de comando.

## Próximos passos

A dona do produto está muito contente com a aplicação por linha de comando, mas acharia melhor se conseguíssimos levar todas essas funcionalidades para o navegador. Ela imagina uma página web com uma caixa de texto que permite que o usuário coloque o número de jogadores e, após submeter esse dado, informe o valor da "aposta cega", atualizando automaticamente quando for apropriado. Assim como a aplicação por linha de comando, ela espera que o usuário possa declarar o vencedor e que isso faça com que as devidas informações sejam salvas no banco de dados.

Descrevendo o projeto dessa forma parece bastante simples, mas sempre precisamos enfatizar que devemos ter uma abordagem *iterativa* pra desenvolver os nossos programas.

Em primeiro lugar, vamos precisar apresentar um HTML. Até agora, todos os nossos *endpoints* HTTP retornaram texto puro ou JSON. Nós *poderíamos* usar as mesmas técnicas que conhecemos (porque, no fim, tanto o texto puro quanto o JSON são strings), mas nós também podemos usar o pacote [html/template](https://golang.org/pkg/html/template/) para uma solução mais limpa.

Nós também temos que ser capazes de enviar mensagens assíncronas para o usuário dizendo `A aposta blind é *y*` sem ter que recarregar o navegador. Para facilitar isso, podemos usar [WebSockets](https://pt.wikipedia.org/wiki/WebSocket).

> WebSocket é uma tecnologia que permite a comunicação bidirecional por canais full-duplex sobre um único socket TCP (Transmission Control Protocol)

Como estamos adotando várias técnicas, é ainda mais importante que façamos o menor trabalho possível primeiro e só então iteramos.

Por causa disso, a primeira coisa que faremos é criar uma página web com um formulário para o usuário salvar um vencedor. Em vez de usar um formulário simples, vamos usar os WebSockets para enviar os dados para o nosso servidor o salvar.

Depois disso, iremos trabalhaor nos alertas cegos, uma vez que já teremos algum código de infraestrutura pronto.

### E os testes para o JavaScript?

Haverá algum JavaScript escrito para cumprir nossa tarefa, mas não vamos escrever testes para ele.

É claro que é possível, mas, em nome da breviedade, não incluíremos quaisquer explicações para isso.

Desculpem, amigos. Peçam para a O'Reilly me pagar para fazer um "Aprenda JavaScript com testes".

## Escreva o teste primeiro

A primeira coisa que precisamos fazer é montar algum HTML para os usuários quando eles acessarem `/jogo`.

Aqui está um lembrete do código no nosso servidor web:

```go
type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
    http.Handler
}

const tipoConteudoJSON = "application/json"

func NovoServidorJogador(armazenamento ArmazenamentoJogador) *ServidorJogador {
    p := new(ServidorJogador)

    p.armazenamento = armazenamento

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(p.manipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(p.manipulaJogadores))

    p.Handler = roteador

    return p
}
```

A maneira *mais fácil* que podemos fazer por agora é checar que recebemos um código `200` quando acessamos o `GET /jogo`.

```go
func TestJogo(t *testing.T) {
    t.Run("GET /jogo retorna 200", func(t *testing.T) {
        servidor := NovoServidorJogador(&EsbocoDeArmazenamentoJogador{})

        requisicao, _ := http.NewRequest(http.MethodGet, "/jogo", nil)
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificaStatus(t, resposta.Code, http.StatusOK)
    })
}
```

## Tente rodar o teste

```
--- FAIL: TestJogo (0.00s)
=== RUN   TestJogo/GET_/game_returns_200
    --- FAIL: TestJogo/GET_/game_returns_200 (0.00s)
        server_test.go:109: não obteve o status correto, obtido 404, esperado 200
```

## Escreva código suficiente para fazer o teste passar

Nosso servidor tem um roteador definido, então deve ser relativamente fácil corrigir isso.

Adicione o seguinte no nosso roteador:

```go
roteador.Handle("/jogo", http.HandlerFunc(p.jogo))
```

E então escreva o método `jogo`:

```go
func (p *ServidorJogador) jogo(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
}
```

## Refatore

O servidor já está bem graças às inserções que fizemos no código já bem refatorado.

Podemos ajeitar ainda mais o teste um pouco ao adicionarmos uma função auxiliar `novaRequisicaoDeJogo` para fazer a requisição para `/jogo`. Tente escrever essa função você mesmo.

```go
func TestJogo(t *testing.T) {
    t.Run("GET /jogo retorna 200", func(t *testing.T) {
        servidor := NovoServidorJogador(&EsbocoDeArmazenamentoJogador{})

        requisicao :=  novaRequisicaoJogo()
        resposta := httptest.NewRecorder()

        servidor.ServeHTTP(resposta, requisicao)

        verificaStatus(t, resposta, http.StatusOK)
    })
}
```

Você também vai notar que mudei o `verificaStatus` para aceitar `resposta` ao invés de `resposta.Code` já que parece combinar melhor.

Agora precisamos que o endpoint retorne um pouco de HTML, e aqui está ele:

```markup
<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <title>Vamos jogar pôquer</title>
</head>
<corpo>
<section id="jogo">
    <div id="declare-vencedor">
        <label for="vencedor">Vencedor</label>
        <input type="text" id="vencedor"/>
        <button id="vencedor-button">Declare vencedor</button>
    </div>
</section>
</corpo>
<script type="application/javascript">

    const submitWinnerButton = document.getElementById('vencedor-button')
    const entradaVencedor = document.getElementById('vencedor')

    if (window['WebSocket']) {
        const conexão = new WebSocket('ws://' + document.location.host + '/ws')

        submitWinnerButton.onclick = event => {
            conexão.send(entradaVencedor.value)
        }
    }
</script>
</html>
```

Temos uma página web bem simples:

* Uma entrada de texto para a pessoa inserir a vitória
* Um botão onde pode-se clicar para declarar quem venceu
* Um pouco de JavaScript para abrir uma conexão WebSocket para nosso servidor e

  assim gerenciar o envio dos dados ao pressionar o botão

`WebSocket` é integrado na maioria dos navegadores modernos, logo não precisamos nos preocupar em instalar bibliotecas. A página web não vai funcionar em navegadores antigos, mas para nosso caso tá tudo bem.

### Como testamos que retornamos a marcação correta?

Existem algumas formas. Como foi enfatizado no decorrer do livro, é importante que os testes que você escreve têm valor o suficiente para justificar o custo.

1. Escreva um teste baseado no navegador, usando algo como Selenium. Esses testes são os mais "realistas" de todas as abordagens porque começam um navegador web de verdade e simula um usuário interagindo com ele. Esses testes podem te dar muita confiança de que seu sistma funciona, mas são mais difíceis e escrever que os testes unitários e muito mais lentos de serem executados. Para os propósitos do nosso produto, isso é exagero.
2. Fazer uma comparação exata de textos. Isso *pode* funcionar, mas esses tipos de testes acabam sendo muito frágeis. No momento que alguém muda a marcação, você vai ter um teste falhando quando na prática nada está *de fato* falhando.
3. Verificar que chamamos o template correto. Vamos usar uma biblioteca de template da biblioteca padrão para servir o HTML (que falamos brevemente) e podemos injetar na *coisa* que gera o HTML e espionar suas chamadas para verificar que estamos fazendo tudo corretamente. Isso teria um impacto no design do nosso código, mas na realidade isso não estaríamos testando algo tão crítico além de verificar se estamos chamando o arquivo de template correto. Dito isso, só vamos ter um template no nosso projeto e a chance de falha aqui parece pequena.

Então, pela primeira vez no livro "Aprenda Go com Testes", não vamos escrever nenhum teste.

Coloque a marcação em um arquivo chamado `jogo.html`.

Na próxima mudança do endoint, vamos apenas escrever o seguinte:

```go
func (p *ServidorJogador) jogo(w http.ResponseWriter, r *http.Request) {
    tmpl, err := template.ParseFiles("jogo.html")

    if err != nil {
        http.Error(w, fmt.Sprintf("problem loading template %s", err.Error()), http.StatusInternalServerError)
        return
    }

    tmpl.Execute(w, nil)
}
```

O [`html/template`](https://golang.org/pkg/html/template/) é um pacote do Go para criar HTML. No nosso caso, chamamos `template.ParseFiles` enviando o caminho do nosso arquivo HTML. Presumindo que não há nenhum erro, chamamos a função `Execute` para "executar" o template, que o escreve para um `ìo.Writer`. No nosso caso, esperamos que o template seja escrito na internet, então enviamos o nosso `http.ResponseWriter`.

Já que não escrevemos um teste, seria prudente testar nosso servidor web manualmente só para ter certeza de que as coisas estão funcionamos como esperamos. Vá para `cmd/webserver` e execute o arquivo `main.go`. Visite `http://localhost:5000/jogo`.

Você *deve* ter obtido um erro sobre não ser capaz de encontrar o template. Você pode ou mudar o caminho para ser relativo à sua pasta, ou pode ter uma cópia de `` `jogo.html `` no diretório `cmd/webserver`. Eu escolho criar um symlink (`ln -s ../../jogo.html jogo.html`) para o arquivo dentro da raiz do projeto para caso eu faça alterações, elas reflitam quando o servidor estiver sendo executado.

Se fizer essa alteração e rodar novamente, deve conseguir ver a interface.

Agora precisamos testar que, quando obtemos uma string sob uma conexão WebSocket para o nosso servidor, declaramos a pessoa como vencedora de um jogo.

## Escreva o teste primeiro

Pela primeia vez, vamos usar uma biblioteca externa para trabalhar com WebSockets.

Rode `go get github.com/gorilla/websocket`.

Isso vai obter o código para a excelente biblioteca [Gorilla WebSocket](https://github.com/gorilla/websocket). Agora podemos atualizar nossos testes para nosso novo requerimento.

```go
t.Run("quando recebemos uma mensagem de um websocket que é vencedor da jogo", func(t *testing.T) {
    armazenamento := &EsbocoDeArmazenamentoJogador{}
    vencedor := "Ruth"
    servidor := httptest.NewServer(NovoServidorJogador(armazenamento))
    defer servidor.Close()

    wsURL := "ws" + strings.TrimPrefix(servidor.URL, "http") + "/ws"

    ws, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
    if err != nil {
        t.Fatalf("não foi possível abrir uma conexão de websocket em %s %v", wsURL, err)
    }
    defer ws.Close()

    if err := ws.WriteMessage(websocket.TextMessage, []byte(vencedor)); err != nil {
        t.Fatalf("não foi possível enviar mensagem na conexão websocket %v", err)
    }

    VerificaVitoriaDoVencedor(t, armazenamento, vencedor)
})
```

Certifique-se que tenha importado o pacote `websocket`. Minha IDE fez isso automaticamente para mim e a sua deve fazer o mesmo.

Para testar o que acontece do navegador, temos que abrir nossa própria conexão WebSocket e escrever nela.

Nossos testes anteriores do servidor apenas chamavam métodos no nosso servidor, mas agora precisamos ter uma conexão persistente nele. Para fazer isso, usamos o `httptest.NewServer`, que recebe um `http.Handler` que vai esperar conexões.

Ao usar `websocket.DefaultDialer.Dial`, tentamos conectar no nosso servidor para então enviar uma mensagem com nosso `vencedor`.

Por fim, verificamos o armazenamento do jogador para certificar que o vencedor foi gravado.

## Execute o teste

```
=== RUN   TestJogo/quando_recebemos_uma_mensagem_via_websocket_que_ha_um_vencedor_de_uma_jogo
    --- FAIL: TestJogo/quando_recebemos_uma_mensagem_via_websocket_que_ha_um_vencedor_de_uma_jogo (0.00s)
        server_test.go:124: não foi possível abrir uma conexão de websocket em ws://127.0.0.1:55838/ws websocket: bad handshake
```

Não mudamos nosso servidor para aceitar conexões WebSocket em `/ws`, então ainda não estamos [apertando as mãos](https://pt.wikipedia.org/wiki/Handshake).

## Escreva código suficiente para fazer o teste passar

Adicione outra linha no nosso roteador:

```go
roteador.Handle("/ws", http.HandlerFunc(p.webSocket))
```

E adicione nosso novo manipulador `webSocket`:

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    upgrader := websocket.Upgrader{
        ReadBufferSize:  1024,
        WriteBufferSize: 1024,
    }
    upgrader.Upgrade(w, r, nil)
}
```

Para aceitar uma conexão WebSocker, precisamos de um método `Upgrade` para atualizar a requisição. Agora, se você executar o teste novamente, o próximo erro deve aparecer.

```
=== RUN   TestJogo/quando_recebemos_uma_mensagem_via_websocket_que_ha_um_vencedor_de_uma_jogo
    --- FAIL: TestJogo/quando_recebemos_uma_mensagem_via_websocket_que_ha_um_vencedor_de_uma_jogo (0.00s)
        server_test.go:132: obtido 0 chamadas paraGravarVitoria esperado 1
```

Agora que temos uma conexão aberta, vamos esperar por uma pensagem e então gravá-la como vencedor.

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    upgrader := websocket.Upgrader{
        ReadBufferSize:  1024,
        WriteBufferSize: 1024,
    }
    conexão, _ := upgrader.Upgrade(w, r, nil)
    _, winnerMsg, _ := conexão.ReadMessage()
    p.armazenamento.GravarVitoria(string(winnerMsg))
}
```

(Sim, estamos ignorando vários erros nesse momento!)

`conexão.ReadMessage()` bloqueia a espera por uma mensagem na conexão. Quando obtivermos uma, vamos usá-la para `GravarVitoria`. Isso finalmente fecharia a conexão WebSocket.

Se tentar executar o teste, ele ainda vai falhar.

O problema está no tempo. Há um atraso entre nossa conexão WebSocket ler a mensagem e gravar a vitória e nosso teste termina sua execução antes disso acontecer. Você pode testar isso colocando um `time.Sleep` curto antes da verificação final.

Vamos continuar com isso por enquanto, mas saiba que colocar sleeps arbitrários em testes **é uma prática muito ruim**.

```go
time.Sleep(10 * time.Millisecond)
VerificaVitoriaDoVencedor(t, armazenamento, vencedor)
```

## Refatore

Cometemos vários pecados para fazer esse teste funcionar tanto no código do servidor quanto no código do teste, mas lembre-se que essa é a forma mais fácil para fazer as coisas funcionarem.

Temos um software horrível e cheio de gambiarras *funcionando* apoiado por testes, então agora temos a liberdade para torná-lo elegante sabendo que não vamos quebrar nada por acidente.

Então, vamos começar com o código do servidor.

Podemos mover o `upgrader` para um valor privado dentro do nosso pacote porque não precisamos redeclará-lo em toda requisição na conexão com o WebSocket.

```go
var atualizadorDeWebsocket = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    conexão, _ := atualizadorDeWebsocket.Upgrade(w, r, nil)
    _, winnerMsg, _ := conexão.ReadMessage()
    p.armazenamento.GravarVitoria(string(winnerMsg))
}
```

Nossa chamada para `template.ParseFiles("jogo.html")` vai ser executada a cada `GET /jogo`, o que significa que vamos usar o sistema de arquivo a cada requisição apesar de não ser necessário parsear o template novamente. Vamos refatorar o código para que possamos fazer o parse do template uma vez em `NovoServidorJogador` ao invés disso. Vamos ter que fazer isso para que nossa função possa retornar um erro caso tenhamos problema ao obter o template do disco ou fazer parse dele.

Agora vamos às mudanças relevantes do `ServidorJogador`:

```go
type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
    http.Handler
    template *template.Template
}

const caminhoTemplateHTML = "jogo.html"

func NovoServidorJogador(armazenamento ArmazenamentoJogador) (*ServidorJogador, error) {
    p := new(ServidorJogador)

    tmpl, err := template.ParseFiles("jogo.html")

    if err != nil {
        return nil, fmt.Errorf("problema ao abrir %s %v", caminhoTemplateHTML, err)
    }

    p.template = tmpl
    p.armazenamento = armazenamento

    roteador := http.NewServeMux()
    roteador.Handle("/liga", http.HandlerFunc(p.manipulaLiga))
    roteador.Handle("/jogadores/", http.HandlerFunc(p.manipulaJogadores))
    roteador.Handle("/jogo", http.HandlerFunc(p.jogo))
    roteador.Handle("/ws", http.HandlerFunc(p.webSocket))

    p.Handler = roteador

    return p, nil
}

func (p *ServidorJogador) jogo(w http.ResponseWriter, r *http.Request) {
    p.template.Execute(w, nil)
}
```

Ao alterar a assinatura de `NovoServidorJogador`, agora temos problemas de compilação. Tente corrigir por si só ou olhe para o código fonte caso para ver a solução.

Para o código de teste, fiz uma função auxiliar chamada `deveFazerServidorJogador(t *testing.T, armazenamento ArmazenamentoJogador) *ServidorJogador` para que eu possa esconder o erro dos testes.

```go
func deveFazerServidorJogador(t *testing.T, armazenamento ArmazenamentoJogador) *ServidorJogador {
    servidor, err := NovoServidorJogador(armazenamento)
    if err != nil {
        t.Fatal("problema ao criar o servidor do jogador", err)
    }
    return servidor
}
```

Da mesma forma, criei outra função auxiliar `deveConectarAoWebSocket` para que eu possa esconder um erro ao criar uma conexão de WebSocket.

```go
func deveConectarAoWebSocket(t *testing.T, url string) *websocket.Conn {
    ws, _, err := websocket.DefaultDialer.Dial(url, nil)

    if err != nil {
        t.Fatalf("não foi possível abrir uma conexão de websocket em %s %v", url, err)
    }

    return ws
}
```

Finalmente, podemos criar uma função auxiliar no nosso código de teste para enviar mensagens:

```go
func escreverMensagemNoWebsocket(t *testing.T, conexão *websocket.Conn, mensagem string) {
    t.Helper()
    if err := conexão.WriteMessage(websocket.TextMessage, []byte(mensagem)); err != nil {
        t.Fatalf("não foi possível enviar mensagem na conexão websocket %v", err)
    }
}
```

Agora que os testes estão passando, tent executar o servidor e declarar alguns vencedores em `/jogo`. Devemos vê-los gravados em `/liga`. Lembre-se que sempre que tivermos um vencedor, vamos *fechar a conexão*, e você vai precisar atualizar a página para abrir a conexão novamente.

Fizemos um formulário simples da web que permite que usuários gravem o vencedor de uma jogo. Vamos iterar nele para fazer com que o usuário possa começar uma jogo inserindo o número de jogadores e o servidor vai mostrar mensagens para o cliente informando-o qual é o valor do blind conforme o tempo passa.

Primeiramente, atualize o `jogo.html` para atualizar o código do lado do cliente para os novos requerimentos:

```markup
<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <title>Vamos jogar pôquer</title>
</head>
<corpo>
<section id="jogo">
    <div id="jogo-start">
        <label for="jogador-count">Número de jogadores</label>
        <input type="number" id="jogador-count"/>
        <button id="start-jogo">Começar</button>
    </div>

    <div id="declare-vencedor">
        <label for="vencedor">Vencedor</label>
        <input type="text" id="vencedor"/>
        <button id="vencedor-button">Declare vencedor</button>
    </div>

    <div id="blind-value"/>
</section>

<section id="jogo-end">
    <h1>Outra ótima jogo de pôquer, pessoal!!</h1>
    <p><a href="/liga">Verifique a tabela da liga</a></p>
</section>

</corpo>
<script type="application/javascript">
    const startGame = document.getElementById('jogo-start')

    const declareWinner = document.getElementById('declare-vencedor')
    const submitWinnerButton = document.getElementById('vencedor-button')
    const entradaVencedor = document.getElementById('vencedor')

    const blindContainer = document.getElementById('blind-value')

    const gameContainer = document.getElementById('jogo')
    const gameEndContainer = document.getElementById('jogo-end')

    declareWinner.hidden = true
    gameEndContainer.hidden = true

    document.getElementById('start-jogo').addEventListener('click', event => {
        startGame.hidden = true
        declareWinner.hidden = false

        const numeroDeJogadores = document.getElementById('jogador-count').value

        if (window['WebSocket']) {
            const conexão = new WebSocket('ws://' + document.location.host + '/ws')

            submitWinnerButton.onclick = event => {
                conexão.send(entradaVencedor.value)
                gameEndContainer.hidden = false
                gameContainer.hidden = true
            }

            conexão.onclose = evt => {
                blindContainer.innerText = 'Connection closed'
            }

            conexão.onmessage = evt => {
                blindContainer.innerText = evt.data
            }

            conexão.onopen = function () {
                conexão.send(numeroDeJogadores)
            }
        }
    })
</script>
</html>
```

As principais alterações envolvem inserir uma seção para definir o número de jogadores e uma seção para mostrar o valor do blind. Temos um pouco de lógica para mostrar/esconder a interface do usuário dependendo da etapa da jogo.

Para qualquer mensagem que recebermos via `conexão.onmessage`, presumimos ser alertas de blind e então definimos o `blindContainer.innerText` de acordo.

Como fazemos para enviar os alertas de blind?No capítulo anterior, mostramos a ideia de `Jogo` para que nosso código CLI possa chamar um `Jogo` e todo o restante se responsabilizaria por agendar os alertas de blind. Isso acabou até sendo uma boa separação de responsabilidades.

```go
type Jogo interface {
    Começar(numeroDeJogadores int)
    Terminar(vencedor string)
}
```

Quando o usuário era requisitado pela CLI pelo número de jogadores, ele precisava `Começar` a jogo, o que ativaria os alertas de blind, e quando o usuario declarava o vencedor, isso iria `Terminar`. Esses sã os mesmos requerimentos que temos agora, só que a obtenção das entradas era diferente; logo, só precisamos reutilizar esse conceito aonde possível.

Nossa implementação "real" de `Jogo` é `TexasHoldem`:

```go
type TexasHoldem struct {
    alertador AlertadorDeBlind
    armazenamento   ArmazenamentoJogador
}
```

Ao enviar um `AlertadorDeBlind`, o `TexasHoldem` pode agendar alertas de blind para enviar para *qualquer lugar*.

```go
type AlertadorDeBlind interface {
    AgendarAlertaPara(duracao time.Duration, quantia int)
}
```

E só para lembrar, aqui está nossa implementação do `AlertadorDeBlind` que usamos na CLI.

```go
func SaidaAlertador(duracao time.Duration, quantia int) {
    time.AfterFunc(duracao, func() {
        fmt.Fprintf(os.Stdout, "Blind agora é %d\n", quantia)
    })
}
```

Isso funciona no CLI porque estamos *sempre esperando para enviar os alertas para `os.Stdout`*, mas isso não vai funcionar no nosso servidor web. Para cada requisição, obtemos um novo `http.ResponseWriter` que então melhoramos para uma `*websocket.Conn`. Logo, não odemos saber quando construímos nossas dependências para onde nossos alertas precisam ir.

Por esse motivo, precisamos mudar o `AlertadorDeBlind.AgendarAlertaPara` para que ele receba um destino paara os alertas para que possamos reutiliza-lo no nosso servidor web.

Abra o `AlertadorDeBlind.go` e adicione o parâmetro para io.Writer\`:

```go
type AlertadorDeBlind interface {
    AgendarAlertaPara(duracao time.Duration, quantia int, para io.Writer)
}

type AlertadorDeBlindFunc func(duracao time.Duration, quantia int, para io.Writer)

func (a AlertadorDeBlindFunc) AgendarAlertaPara(duracao time.Duration, quantia int, para io.Writer) {
    a(duracao, quantia, para)
}
```

A ideia de um `SaidaAlertador` não encaixa bem no nosso modelo, então vamos apenas renomeá-lo para `Alertador`:

```go
func Alertador(duracao time.Duration, quantia int, para io.Writer) {
    time.AfterFunc(duracao, func() {
        fmt.Fprintf(para, "Blind agora é %d\n", quantia)
    })
}
```

Se tentar compilar, haverá uma falha em `TexasHoldem` porque estamos chamando `AgendarAlertaPara` sem uma descrição. Só para deixar tudo compilando novamente, vamos escrevê-lo para `os.Stdout`.

Execute os testes e eles vão falhar porque o `AlertadorDeBlindEspiao` não implementa mais o `AlertadorDeBlind`. Corrija isso atualizando a assinatura de `AgendarAlertaPara`, execute os testes e todos devem estar passando.

Não faz sentido nenhum que o `TexasHoldem` saiba para onde enviar os alertas de blind. Agora, vamos atualizar o `Jogo` para que quando você começa uma jogo, declare *para onde* os alertas devem ir.

```go
type Jogo interface {
    Começar(numeroDeJogadores int, destinoDosAlertas io.Writer)
    Terminar(vencedor string)
}
```

Deixe o compilador te dizer o que precisa ser corrigido. As alterações não são tão ruins:

* Atualize o `TexasHoldem` para que implemente `Jogo` corretamente
* No `CLI`, quando começamos a jogo, preciamos passar nosssa propriedade `saida` (`cli.jogo.Começar(numeroDeJogadores, cli.saida`)
* No teste do `TexasHoldem`, precisamos usar `jogo.Começar(5, ioutil.Discard)` para corrigir o problema de compilação e configurar a saída do alerta para ser descartada&#x20;

Se tiver feito tudo certo, todos os testes devem passar! Agora podemos usar `Jogo` dentro do `Servidor`.

## Escreva os testes primeiro

Os requerimentos de `CLI` e `Servidor` são os mesmos! É apenas o mecanismo de entrega que é diferente.

Vamos dar uma olhada no nosso teste do `CLI` para inspiração.

```go
t.Run("começa jogo com 3 jogadores e termina jogo com 'Chris' como vencedor", func(t *testing.T) {
    jogo := &JogoEspiao{}

    saida := &bytes.Buffer{}
    in := usuarioEnvia("3", "Chris venceu")

    poquer.NovaCLI(in, saida, jogo).JogarPoquer()

    verificaMensagensEnviadasParaUsuario(t, saida, poquer.PromptJogador)
    verificaJogoComeçadoCom(t, jogo, 3)
    verificaTerminosChamadosCom(t, jogo, "Chris")
})
```

Parece que devemos ser capazes de testar um resultado semelhante usando `JogoEspiao`.

Substitua o antigo teste de websocket com o seguinte:

```go
t.Run("começa uma jogo com 3 jogadores e declara Ruth vencedora", func(t *testing.T) {
    jogo := &poquer.JogoEspiao{}
    vencedor := "Ruth"
    servidor := httptest.NewServer(deveFazerServidorJogador(t, ArmazenamentoJogadorTosco, jogo))
    ws := deveConectarAoWebSocket(t, "ws"+strings.TrimPrefix(servidor.URL, "http")+"/ws")

    defer servidor.Close()
    defer ws.Close()

    escreverMensagemNoWebsocket(t, ws, "3")
    escreverMensagemNoWebsocket(t, ws, vencedor)

    time.Sleep(10 * time.Millisecond)
    verificaJogoComeçadoCom(t, jogo, 3)
    verificaTerminosChamadosCom(t, jogo, vencedor)
})
```

* Conforme discutidos, criamos um espião de `Jogo` e passamos para o `` `deveFazerServidorJogador `` (certifique-se de atualizar a função auxiliar para isso).
* Depois, enviamos mensagens no web socket para uma jogo.
* Por mim, verificamos que a jogo começou e finalizamos com o que esperamos.

## Execute o teste

Você terá vários erros de compilação envolvendo `deveFazerServidorJogador` em outros testes. Crie uma variável não exportada `jogoTosco` e use-a em todos os testes que não estão compilando:

```go
var (
    jogoTosco = &JogoEspiao{}
)
```

O erro final se encontra onde estamos tentando passar em `Jogo`, pois `NovoServidorJogador` ainda não o suporta:

```
./server_test.go:21:38: too many arguments in call para "github.com/larien/aprenda-go-com-testes/WebSockets/v2".NovoServidorJogador
    have ("github.com/larien/aprenda-go-com-testes/WebSockets/v2".ArmazenamentoJogador, "github.com/larien/aprenda-go-com-testes/WebSockets/v2".Jogo)
    esperado ("github.com/larien/aprenda-go-com-testes/WebSockets/v2".ArmazenamentoJogador)
```

## Escreva o mínimo de código possível para o teste funcionar e verifique a saída do teste falhado

Basta adicionar um argumento por enquanto para fazer o teste funcionar:

```go
func NovoServidorJogador(armazenamento ArmazenamentoJogador, jogo Jogo) (*ServidorJogador, error) {
```

Finalmente!

```
=== RUN   TestJogo/começa_um_jogo_com_3_jogadores_e_declara_Ruth_a_vencedora
--- FAIL: TestJogo (0.01s)
    --- FAIL: TestJogo/começa_um_jogo_com_3_jogadores_e_declara_Ruth_a_vencedora (0.01s)
        server_test.go:146: esperava Começar chamado com 3 mas obteve 0
        server_test.go:147: esperava Terminar chamado com 'Ruth' mas obteve ''
FAIL
```

## Escreva código suficiente para fazer o teste passar

Precisamos adicionar `Jogo` como campo para `ServidorJogador` para que possamos usá-lo quando ele obtiver requisições.

```go
type ServidorJogador struct {
    armazenamento ArmazenamentoJogador
    http.Handler
    template *template.Template
    jogo Jogo
}
```

(Já temos um método chamado `jogo`, então é só renomeá-lo para `jogarJogo`)

A seguir, vamos atribui-lo no nosso construtor:

```go
func NovoServidorJogador(armazenamento ArmazenamentoJogador, jogo Jogo) (*ServidorJogador, error) {
    p := new(ServidorJogador)

    tmpl, err := template.ParseFiles("jogo.html")

    if err != nil {
        return nil, fmt.Errorf("problema ao abrir %s %v", caminhoTemplateHTML, err)
    }

    p.jogo = jogo

    // etc
```

Agora podemos usar nosso `Jogo` dentro de `webSocket`.

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    conexão, _ := atualizadorDeWebsocket.Upgrade(w, r, nil)

    _, mensagemNumeroDeJogadores, _ := conexão.ReadMessage()
    numeroDeJogadores, _ := strconv.Atoi(string(mensagemNumeroDeJogadores))
    p.jogo.Começar(numeroDeJogadores, ioutil.Discard) //todo: Não descartar as mensagens de blind!

    _, vencedor, _ := conexão.ReadMessage()
    p.jogo.Terminar(string(vencedor))
}
```

Uhul! Os testes estão passando.

Não vamos enviar as mensagens de blind para nenhum lugar *por enquanto* já que precisamos de um tempo para pensar nisso. Quando chamamos `jogo.Começar`, enviamos os dados para `ioutil.Discard` que vai apenar descartar qualquer mensagem escrita nele.

Por enquanto, vamos iniciar o servidor. Você vai precisar atualizar a `` `main.go `` para passar um `Jogo` para o `ServidorJogador`:

```go
func main() {
    db, err := os.OpenFile(nomeArquivoBaseDeDados, os.O_RDWR|os.O_CREATE, 0666)

    if err != nil {
        log.Fatalf("problema ao abrir %s %v", nomeArquivoBaseDeDados, err)
    }

    armazenamento, err := poquer.NovoSistemaArquivoArmazenamentoJogador(db)

    if err != nil {
        log.Fatalf("problema ao criar sistema de arquivo de armazenamento do jogador, %v ", err)
    }

    jogo := poquer.NovoTexasHoldem(poquer.AlertadorDeBlindFunc(poquer.Alertador), armazenamento)

    servidor, err := poquer.NovoServidorJogador(armazenamento, jogo)

    if err != nil {
        log.Fatalf("problema ao criar o servidor do jogador %v", err)
    }

    if err := http.ListenAndServe(":5000", servidor); err != nil {
        log.Fatalf("não foi possível ouvir na porta 5000 %v", err)
    }
}
```

Tirando o fato de que não temos alertas de blind por enquanto, a aplicação funciona! Conseguimos reutilizar `Jogo` com `ServidorJogador` e ele toma conta dos detalhes. Quando descobrirmos como enviar mensagens de blind atraves de web sockets ao invés de descartá-las, tudo *deve* ficar pronto.

Antes disso, vamos mexer um pouco no código.

## Refatore

A forma que estamos usando WebSocker é bem básica e a mnnipulação de erro é bem fraca, então gostaria de encapsular isso em um tipo só para remover essa bagunça do código do servidor. Precisaremos revisitar isso depois, mas por enqaunto isso vai melhorar um pouco as coisas.

```go
type websocketServidorJogador struct {
    *websocket.Conn
}

func novoWebsocketServidorJogador(w http.ResponseWriter, r *http.Request) *websocketServidorJogador {
    conexão, err := atualizadorDeWebsocket.Upgrade(w, r, nil)

    if err != nil {
        log.Printf("houve um problema ao atualizar a conexão para WebSockets %v\n", err)
    }

    return &websocketServidorJogador{conexão}
}

func (w *websocketServidorJogador) EsperarPelaMensagem() string {
    _, msg, err := w.ReadMessage()
    if err != nil {
        log.Printf("erro ao ler do websocket %v\n", err)
    }
    return string(msg)
}
```

Agora o código do servidor fica um pouco mais simples:

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    ws := novoWebsocketServidorJogador(w, r)

    mensagemNumeroDeJogadores := ws.EsperarPelaMensagem()
    numeroDeJogadores, _ := strconv.Atoi(mensagemNumeroDeJogadores)
    p.jogo.Começar(numeroDeJogadores, ioutil.Discard) //todo: Não descartar as mensagens de blind!

    vencedor := ws.EsperarPelaMensagem()
    p.jogo.Terminar(vencedor)
}
```

Quando descobrirmos como não descartar as mensagens de blind teremos terminado essa etapa.

### *Não* vamos escrever um teste!

Às vezes, quando não temos certeza de como vamos fazer algo, é melhor apenas brincar e testar coisas diferentes! Tenha certeza de que seu trabalho está salvo primeiro porque quando descobrirmos o que fazer, vamos implementá-lo junto de um teste.

A linha problemática do código que temos é:

```go
p.jogo.Começar(numeroDeJogadores, ioutil.Discard) //todo: Não descartar as mensagens de blind!
```

Precisamos passar um `io.Writer` para a jogo para ter aonde escrever os alertas be blind.

Não seria legal se apenas precisássemos passar o nosso `websocketServidorJogador` de antes? É o nosso wrapper em torno do nosso WebSocket, então *parece* que devemos ser capazes de enviá-lo para que nosso `Jogo` seja capaz de enviar mensagens para ele.

Vamos tentar:

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    ws := novoWebsocketServidorJogador(w, r)

    mensagemNumeroDeJogadores := ws.EsperarPelaMensagem()
    numeroDeJogadores, _ := strconv.Atoi(mensagemNumeroDeJogadores)
    p.jogo.Começar(numeroDeJogadores, ws)
    //etc...
```

O compilador reclama:

```
./servidor.go:71:14: cannot use ws (type *websocketServidorJogador) as type io.Writer in argument para p.jogo.Começar:
    *websocketServidorJogador does not implement io.Writer (missing Write method)
```

Parece que a coisa óbvia a se fazer é fazer com que o `websocketServidorJogador` *implementa* o `io.Writer`. Para fazer isso, precisamos usar do `*websocket.Conn` para ussar a escrita de mensagem `WriteMessage` para enviar a mensagem para o websocket.

```go
func (w *websocketServidorJogador) Write(p []byte) (n int, err error) {
    err = w.WriteMessage(1, p)

    if err != nil {
        return 0, err
    }

    return len(p), nil
}
```

Isso parece fácil demais! Execute a aplicação para ver se funciona.

Mas antes edite o `TexasHoldem` para que o tempo de incremento do blind seja mais curto para que você possa ver as coisas em ação:

```go
incrementoDeBlind := time.Duration(5+numeroDeJogadores) * time.Second // (ao invés de um minuto)
```

As coisas devem estar funcionando! A quantidade do blind é incrementada no computador como se fosse mágica.

Agora vamos reverter o código e pensar como testá-lo. Para *implementar* isso tudo o que precisamos fazer foi passar o `websocketServidorJogador` para `ComeçarJogo` no lugar do `ioutil.Discard`, então isso faz parecer que tenhamos que espionar a chamada para verificar se ela funciona.

Espionar é ótimo e nos ajuda a verificar os detalhes de implementação, mas sempre devemos favorecer o teste do comportamento *real* se possível, porque caso seja necessário refatorar isso os testes espiões são os primeiros a começar a falhar por geralmente verificarem os detalhes de implementação que estamos tentando alterar.

Nosso teste atualmente abre uma conexão websocket para nosso servidor em execução e envia mensagens para fazê-lo efetuar ações. De forma semelhante, devemos ser capazes de testar as mensagens que o nosso servidor envia de volta para a conexão de websocket.

## Escreva o teste primeiro

Vamos editar nosso teste existente.

Atualmente, nosso `JogoEspiao` não envia nenhum dado para a `saida` quando você chama `Começar`. Devemos alterar isso para que possamos configurá-lo para enviar uma mensagem e então verificar se a mensagem é enviada para o websocket. Isso deve nos dar confiança que configuramos as coisas corretamente enquanto ainda exercitamos o comportamento real do que esperamos.

```go
type JogoEspiao struct {
    ComecouASerChamado     bool
    ComecouASerChamadoCom int
    AlertaDeBlind      []byte

    TerminouDeSerChamado   bool
    TerminouDeSerChamadoCom string
}
```

Adicione o campo de `AlertaDeBlind`.

Atualize o `Começar` do `JogoEspiao` para enviar a mensagem para a `saída`.

```go
func (j *JogoEspiao) Começar(numeroDeJogadores int, saida io.Writer) {
    j.ComecouASerChamado = true
    j.ComecouASerChamadoCom = numeroDeJogadores
    saida.Write(j.AlertaDeBlind)
}
```

Agora isso significa que quando usarmos o `ServidorJogador`, quando ele tentar `Começar` o jogo, deve acabar enviando mensagens pelo websocket se as coisas estiverem funcionando direito.

Finalmente podemos atualizar o teste:

```go
t.Run("começa uma artida com  3 jogadores, envia alguns alertas de blind no websocket e declara Ruth como vencedora", func(t *testing.T) {
    alertaDeBlindEsperado := "Blind é 100"
    vencedor := "Ruth"

    jogo := &JogoEspiao{AlertaDeBlind: []byte(alertaDeBlindEsperado)}
    servidor := httptest.NewServer(deveFazerServidorJogador(t, ArmazenamentoJogadorTosco, jogo))
    ws := deveConectarAoWebSocket(t, "ws"+strings.TrimPrefix(servidor.URL, "http")+"/ws")

    defer servidor.Close()
    defer ws.Close()

    escreverMensagemNoWebsocket(t, ws, "3")
    escreverMensagemNoWebsocket(t, ws, vencedor)

    time.Sleep(10 * time.Millisecond)
    verificaJogoComeçadoCom(t, jogo, 3)
    verificaTerminosChamadosCom(t, jogo, vencedor)

    _, alertaDeBlindObtido, _ := ws.ReadMessage()

    if string(alertaDeBlindObtido) != alertaDeBlindEsperado {
        t.Errorf("alerta de blind obtido '%s', esperado '%s'", string(alertaDeBlindObtido), alertaDeBlindEsperado)
    }
})
```

* Adicionamos um `alertaDeBlindEsperado` e configuramos nosso `JogoEspiao` para enviá-lo para a `saida` se `Começar` for chamado.
* Esperamos que ela seja enviada na conexão do websocket, então adicionamos uma chamada para `ws.ReadMessage()` para esperar por uma mensagem ser enviada e então verificamos se é aquela que esperamos.

## Execute o teste

Talvez você pense que o teste demora demais. Isso acontece porque o `` `ws.ReadMessage() `` vai bloqueá-lo até obter a mensagem, que nunca vai chegar.

## Escreva o mínimo de código necessário para o teste ser executado e verifique a saída do teste falhando

Nunca devemos ter testes que demoram, então vamos apresentar uma nova forma de lidar com coigo que esperamos com um timeout.

```go
func within(t *testing.T, d time.Duration, assert func()) {
    t.Helper()

    done := make(chan struct{}, 1)

    go func() {
        assert()
        done <- struct{}{}
    }()

    select {
    case <-time.After(d):
        t.Error("timed out")
    case <-done:
    }
}
```

O que o `within` faz é pegar uma função `assert` como argumento e então o executa dentro de uma goroutine. Se/Quando a função termina, ela avisa que terminou através do canal `done`.

Enquanto isso acontece, usamos uma declaração `select` que nos permite esperar por um canal para enviar uma mensagem. A partir daí é uma corrida entre a função de `assert` e o `time.After` que vai enviar um sinal qunado a duração chega ao fim.

Por mim, fiz uma função auxiliar para a nossa verificação so para melhorar um pouco as coisas:

```go
func verificaSeWebSocketObteveMensagem(t *testing.T, ws *websocket.Conn, esperado string) {
    _, msg, _ := ws.ReadMessage()
    if string(msg) != esperado {
        t.Errorf(`obtido "%s", esperado "%s"`, string(msg), esperado)
    }
}
```

É assim que o teste fica agora:

```go
t.Run("começa uma artida com  3 jogadores, envia alguns alertas de blind no websocket e declara Ruth como vencedora", func(t *testing.T) {
    alertaDeBlindEsperado := "Blind é 100"
    vencedor := "Ruth"

    jogo := &JogoEspiao{AlertaDeBlind: []byte(alertaDeBlindEsperado)}
    servidor := httptest.NewServer(deveFazerServidorJogador(t, ArmazenamentoJogadorTosco, jogo))
    ws := deveConectarAoWebSocket(t, "ws"+strings.TrimPrefix(servidor.URL, "http")+"/ws")

    defer servidor.Close()
    defer ws.Close()

    escreverMensagemNoWebsocket(t, ws, "3")
    escreverMensagemNoWebsocket(t, ws, vencedor)

    time.Sleep(tenMS)

    verificaJogoComeçadoCom(t, jogo, 3)
    verificaTerminosChamadosCom(t, jogo, vencedor)
    within(t, tenMS, func() { verificaSeWebSocketObteveMensagem(t, ws, alertaDeBlindEsperado) })
})
```

Agora se você rodar o teste...

```
=== RUN   TestJogo
=== RUN   TestJogo/começa_um_jogo_com_3_jogadores,envia_alguns_alertas_de_blind_para_o_websocket_e_declara_Ruth_como_vencedora
--- FAIL: TestJogo (0.02s)
    --- FAIL: TestJogo/começa_um_jogo_com_3_jogadores,envia_alguns_alertas_de_blind_para_o_websocket_e_declara_Ruth_como_vencedora (0.02s)
        server_test.go:143: timed out
        server_test.go:150: obtido "", esperado "Blind é 100"
```

## Escreva código suficiente para fazer o teste passar

Finalmente podemos alterar o código do nosso servidor para que ele envie a mensagem para nossa conexão com o WebSocket para a jogo quando ela começa:

```go
func (p *ServidorJogador) webSocket(w http.ResponseWriter, r *http.Request) {
    ws := novoWebsocketServidorJogador(w, r)

    mensagemNumeroDeJogadores := ws.EsperarPelaMensagem()
    numeroDeJogadores, _ := strconv.Atoi(mensagemNumeroDeJogadores)
    p.jogo.Começar(numeroDeJogadores, ws)

    vencedor := ws.EsperarPelaMensagem()
    p.jogo.Terminar(vencedor)
}
```

## Refatorar

O código do servidor sofreu uma mudança bem pequena, então não tem muito o que mudar aqui, mas o código de teste ainda tem uma chamada `time.Sleep` porque temos que esperar até que o nosso servidor termina sua tarefa assíncronamente.

Podemos refatorar nossas funções auxiliares `verificaJogoComeçadoCom` e `verificaTerminosChamadosCom` para que possam tentar as verificações novamente logo após falharem.

Abaixo esta como fazer isso com o `verificaTerminosChamadosCom` e você pode usar a mesma abordagem para a outra função auxiliar.

```go
func verificaTerminosChamadosCom(t *testing.T, jogo *JogoEspiao, vencedor string) {
    t.Helper()

    passou := tentarNovamenteAte(500*time.Millisecond, func() bool {
        return jogo.TerminouDeSerChamadoCom == vencedor
    })

    if !passou {
        t.Errorf("esperava chamada de término com '%s' mas obteve '%s' ", vencedor, jogo.TerminouDeSerChamadoCom)
    }
}
```

Aqui está como `tentarNovamenteAte` está definida:

```go
func tentarNovamenteAte(d time.Duration, f func() bool) bool {
    deadline := time.Now().Add(d)
    for time.Now().Before(deadline) {
        if f() {
            return true
        }
    }
    return false
}
```

## Resumindo

Nossa aplicação agora está completa. Um jogo de pôquer agora pode ser iniciado pelo navegador web e os usuários são informados sobre o valor da aposta cega enquanto o tempo passa por meio de WebSockets. Quando o jogo for encerrado, eles podem salvar o vencedor, o que é persistente uma vez que estamos usando o código que escrevemos há alguns capítulos atrás. Os jogadores podem descobrir quem é o melhor (ou o mais sortudo) jogador de pôquer utilizando o endpoint `/liga` do nosso website.

No decorrer da nossa jornada cometemos diversos erros, mas com o fluxo de desenvolvimento orientado a testes (TDD) nunca estivemos com um programa que não rodava de jeito nenhum. Somos livres para continuar iterando e experimentando outras coisas.

O capítulo final vai recapitular o nosso método, o design que alcançamos e por fim apertar alguns nós que possam parecer soltos.

Nós cobrimos algumas coisas nesse capítulo.

### WebSockets

* Maneira conveniente de enviar mensagens entre clientes e servidores sem precisar

  que o cliente fique sondando (?) o servidor. O código que fizemos tanto do cliente quanto

  do servidor são muito simples.
* É trivial para testar, mas você tem que se atentar com a natureza assíncrona dos testes.

### Lidando com código em testes qeu podem ter sido atrasados ou nunca terem terminado

* Crie funções utilitárias para tentar verificações novamente e adicione timeouts.
* Podemos usar go routines para certificar que as verificações não bloqueiam nada e então usar canais para deixá-los sinalizar se tiverem terminado ou não;
* O pacote `time` tem algumas funções úteis que também enviam sinais para canais sobre eventos no tempo para que possamos definir timeouts.


# OS Exec

[**You can find all the code here**](https://github.com/larien/aprenda-go-com-testes/tree/master/duvidas-da-comunidade/os-exec)

[keith6014](https://www.reddit.com/user/keith6014) asks on [reddit](https://www.reddit.com/r/golang/comments/aaz8ji/testdata_and_function_setup_help/)

> I am executing a command using os/exec.Command() which generated XML data. The command will be executed in a function called GetData().
>
> In order to test GetData(), I have some testdata which I created.
>
> In my \_test.go I have a TestGetData which calls GetData() but that will use os.exec, instead I would like for it to use my testdata.
>
> What is a good way to achieve this? When calling GetData should I have a "test" flag mode so it will read a file ie GetData(mode string)?

A few things

* When something is difficult to test, it's often due to the separation of concerns not being quite right
* Dont add "test modes" into your code, instead use [Dependency Injection](/aprenda-go-com-testes/primeiros-passos-com-go/injecao-de-dependencia) so that you can model your dependencies and separate concerns.&#x20;

I have taken the liberty of guessing what the code might look like

```go
type Payload struct {
    Message string `xml:"message"`
}

func GetData() string {
    cmd := exec.Command("cat", "msg.xml")

    out, _ := cmd.StdoutPipe()
    var payload Payload
    decoder := xml.NewDecoder(out)

    // these 3 can return errors but I'm ignoring for brevity
    cmd.Start()
    decoder.Decode(&payload)
    cmd.Wait()

    return strings.ToUpper(payload.Message)
}
```

* It uses `exec.Command` which allows you to execute an external command to the process
* We capture the output in `cmd.StdoutPipe` which returns us a `io.ReadCloser` (this will become important)
* The rest of the code is more or less copy and pasted from the [excellent documentation](https://golang.org/pkg/os/exec/#example_Cmd_StdoutPipe).&#x20;
  * We capture any output from stdout into an `io.ReadCloser` and then we `Start` the command and then wait for all the data to be read by calling `Wait`. In between those two calls we decode the data into our `Payload` struct.

Here is what is contained inside `msg.xml`

```markup
<payload>
    <message>Happy New Year!</message>
</payload>
```

I wrote a simple test to show it in action

```go
func TestGetData(t *testing.T) {
    got := GetData()
    want := "HAPPY NEW YEAR!"

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }
}
```

## Testable code

Testable code is decoupled and single purpose. To me it feels like there are two main concerns for this code

1. Retrieving the raw XML data
2. Decoding the XML data and applying our business logic (in this case `strings.ToUpper` on the `<message>`)

The first part is just copying the example from the standard lib.

The second part is where we have our business logic and by looking at the code we can see where the "seam" in our logic starts; it's where we get our `io.ReadCloser`. We can use this existing abstraction to separate concerns and make our code testable.

**The problem with GetData is the business logic is coupled with the means of getting the XML. To make our design better we need to decouple them**

Our `TestGetData` can act as our integration test between our two concerns so we'll keep hold of that to make sure it keeps working.

Here is what the newly separated code looks like

```go
type Payload struct {
    Message string `xml:"message"`
}

func GetData(data io.Reader) string {
    var payload Payload
    xml.NewDecoder(data).Decode(&payload)
    return strings.ToUpper(payload.Message)
}

func getXMLFromCommand() io.Reader {
    cmd := exec.Command("cat", "msg.xml")
    out, _ := cmd.StdoutPipe()

    cmd.Start()
    data, _ := ioutil.ReadAll(out)
    cmd.Wait()

    return bytes.NewReader(data)
}

func TestGetDataIntegration(t *testing.T) {
    got := GetData(getXMLFromCommand())
    want := "HAPPY NEW YEAR!"

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }
}
```

Now that `GetData` takes its input from just an `io.Reader` we have made it testable and it is no longer concerned how the data is retrieved; people can re-use the function with anything that returns an `io.Reader` (which is extremely common). For example we could start fetching the XML from a URL instead of the command line.

```go
func TestGetData(t *testing.T) {
    input := strings.NewReader(`
<payload>
    <message>Cats are the best animal</message>
</payload>`)

    got := GetData(input)
    want := "CATS ARE THE BEST ANIMAL"

    if got != want {
        t.Errorf("got '%s', want '%s'", got, want)
    }
}
```

Here is an example of a unit test for `GetData`.

By separating the concerns and using existing abstractions within Go testing our important business logic is a breeze.


# Tipos de erro

[**You can find all the code here**](https://github.com/larien/aprenda-go-com-testes/tree/master/duvidas-da-comunidade/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.**

Pedro on the Gopher Slack asks

> If I’m creating an error like `fmt.Errorf("%s must be foo, got %s", bar, baz)`, is there a way to test equality without comparing the string value?

Let's make up a function to help explore this idea.

```go
// DumbGetter will get the string body of url if it gets a 200
func DumbGetter(url string) (string, error) {
    res, err := http.Get(url)

    if err != nil {
        return "", fmt.Errorf("problem fetching from %s, %v", url, err)
    }

    if res.StatusCode != http.StatusOK {
        return "", fmt.Errorf("did not get 200 from %s, got %d", url, res.StatusCode)
    }

    defer res.Body.Close()
    body, _ := ioutil.ReadAll(res.Body) // ignoring err for brevity

    return string(body), nil
}
```

It's not uncommon to write a function that might fail for different reasons and we want to make sure we handle each scenario correctly.

As Pedro says, we *could* write a test for the status error like so.

```go
t.Run("when you dont 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()

    _, err := DumbGetter(svr.URL)

    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()

    if got != want {
        t.Errorf(`got "%v", want "%v"`, got, want)
    }
})
```

This test creates a server which always returns `StatusTeapot` and then we use its URL as the argument to `DumbGetter` so we can see it handles non `200` responses correctly.

## Problems with this way of testing

This book tries to emphasise *listen to your tests* and this test doesn't *feel* good:

* We're constructing the same string as production code does to test it
* It's annoying to read and write
* Is the exact error message string what we're *actually concerned with* ?

What does this tell us? The ergonomics of our test would be reflected on another bit of code trying to use our code.

How does a user of our code react to the specific kind of errors we return? The best they can do is look at the error string which is extremely error prone and horrible to write.

## What we should do

With TDD we have the benefit of getting into the mindset of:

> How would *I* want to use this code?

What we could do for `DumbGetter` is provide a way for users to use the type system to understand what kind of error has happened.

What if `DumbGetter` could return us something like

```go
type BadStatusError struct {
    URL    string
    Status int
}
```

Rather than a magical string, we have actual *data* to work with.

Let's change our existing test to reflect this need

```go
t.Run("when you dont 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()

    _, err := DumbGetter(svr.URL)

    if err == nil {
        t.Fatal("expected an error")
    }

    got, isStatusErr := err.(BadStatusError)

    if !isStatusErr {
        t.Fatalf("was not a BadStatusError, got %T", err)
    }

    want := BadStatusError{URL:svr.URL, Status:http.StatusTeapot}

    if got != want {
        t.Errorf("got %v, want %v", got, want)
    }
})
```

We'll have to make `BadStatusError` implement the error interface.

```go
func (b BadStatusError) Error() string {
    return fmt.Sprintf("did not get 200 from %s, got %d", b.URL, b.Status)
}
```

### What does the test do?

Instead of checking the exact string of the error, we are doing a [type assertion](https://tour.golang.org/methods/15) on the error to see if it is a `BadStatusError`. This reflects our desire for the *kind* of error clearer. Assuming the assertion passes we can then check the properties of the error are correct.

When we run the test, it tells us we didn't return the right kind of error

```
--- FAIL: TestDumbGetter (0.00s)
    --- FAIL: TestDumbGetter/when_you_dont_get_a_200_you_get_a_status_error (0.00s)
        error-types_test.go:56: was not a BadStatusError, got *errors.errorString
```

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}
}
```

This change has had some *real positive effects*

* Our `DumbGetter` function has become simper, it's no longer concerned with the intricacies of an error string, it just creates a `BadStatusError`.
* Our tests now reflect (and document) what a user of our code *could* do if they decided they wanted to do some more sophisticated error handling than just logging. Just do a type assertion and then you get easy access to the properties of the error.&#x20;
* It is still "just" an `error`, so if they choose to they can pass it up the call stack or log it like any other `error`.

## Wrapping up

If you find yourself testing for multiple error conditions dont fall in to the trap of comparing the error messages.

This leads to flaky and difficult to read/write tests and it reflects the difficulties the users of your code will have if they also need to start doing things differently depending on the kind of errors that have occurred.

Always make sure your tests reflect how *you'd* like to use your code, so in this respect consider creating error types to encapsulate your kinds of errors. This makes handling different kinds of errors easier for users of your code and also makes writing your error handling code simpler and easier to read.


# Por que testes unitários?

[Vejam um vídeo meu falando sobre esse assunto](https://www.youtube.com/watch?v=Kwtit8ZEK7U)

Se não gostar muito de vídeos, aqui vai o artigo relacionado a isso.

## Software

A promessa do software é que ele pode mudar. É por isso que é chamado de \_soft\_ware: é mais maleável se comparado ao hardware. Uma boa equipe de engenharia deve ser um componente incrível para uma empresa, criando sistemas que podem evoluir com um negócio para manter seu valor de entrega.

Então por que somos tão ruins nisso? Quantos projetos que você ouve falar sobre que ultrapassam o nível da falha? Ou viram "legado" e precisam ser totalmente recriados (e a reescrita também acaba falhando)!

Mas como é que um software "falha"? Não dá para ele apenas ser modificado até estar correto? É isso que prometemos!

Muita gente costuma escolher o Go para criar sistemas porque a linguagem teve várias decisões que evitam que o software vire legado.

* Comparado à minha antiga vida de Scala onde [descrevi como é fácil acabar se dando mal com a linguagem](http://www.quii.co.uk/Scala_-_Just_enough_rope_to_hang_yourself), o Go tem apenas 25 palavras-chave. *Muitos* sistemas podem ser criados a partir da biblioteca padrão e alguns outros pacotes pequenos. O que se espera é que com Go você possa escrever código, voltar a vê-lo 6 meses depois e ele ainda fazer sentido.
* As ferramentas relacionadas a testes, benchmarking, linting e shipping são incríveis se comparadas à maioria das alternativas.
* A biblioteca padrão é brilhante.
* Velocidade de compilação muito rápida para loops de feedback mais frequentes.
* A famigerada promessa da compatibilidade. Parece que Go vai receber `generics` e outras funcionalidades no futuro, mas os mantenedores prometeram que mesmo o código Go que você escreveu cinco anos atrás ainda vai compilar e funcionar. Eu literalmente passei semanas atualizando um projeto em Scala da versão 2.8 para a 2.10.

Com todas essas propriedades ótimas, ainda podemos acabar criando sistemas terríveis. Por isso, precisamos aplicar lições de engenharia de software que se aplicam independente do quão maravilhosa (ou não) sua linguagem seja.

Em 1974, um engenheiro de software esperto chamado [Manny Lehman](https://pt.wikipedia.org/wiki/Meir_M._Lehman) escreveu as [leis de Lehman para a evolução do software](https://www.baguete.com.br/colunas/jorge-horacio-audy/17/04/2014/as-8-leis-de-lehman-foram-o-manifesto-do-seculo-xx).

> As leis descrevem um equilíbrio entre o desenvolvimento de software em uma ponta e a diminuição do progresso em outra.

É importante entender esses extremos para não acabar em um ciclo infinito de entregar sistemas que se tornam em legado e precisam ser reescritos.

## Lei da Mudança Contínua

> Qualquer software utilizado no mundo real precisa se adaptar ou vai se tornar cada vez mais obsoleto.

Parece óbvio que um software *precisa* mudar ou acaba se tornando menos útil, mas quantas vezes isso é ignorado?

Muitas equipes são incentivadas a entregar um projeto em uma data específica e passar para o próximo projeto. Se o software tiver "sorte", vai acabar na mão de outro grupo de pessoas para mantê-lo, mas é claro que nenhuma dessas pessoas o escreveu.

As pessoas se preocupam em escolher um framework que vai ajudá-las a "entregar rapidamente", mas não focam na longevidade do sistema em termos de como precisa ser evoluído.

Mesmo se você for um engenheiro de software incrível, ainda vai cair na armadilha de não saber que futuro aguarda seu software. Já que o negócio muda, o código brilhante que você escreveu já não vai mais ser relevante.

Lehman estava contudo nos anos 70, porque nos deu outra lei para quebrarmos a cabeça.

## Lei da Complexidade Crescente

> Enquanto o softwate evolui, sua complexidade aumenta. A não ser que um esforço seja investido para reduzi-la.

O que ele diz aqui é que não podemos ter equipes de software para funcionar apenas como fábricas de funcionalidades, inserindo mais e mais funcionalidades no software para que ele possa sobreviver a longo prazo.

Nós **temos** que lidar com a complexidade do sistema conforme o conhecimento do nosso domínio muda.

## Refatoração

Existem *diversas* facetas na engenharia de software que mantêm um software maleável, como:

* Capacitação do desenvolvimento
* Em termos gerais, código "bom". Separação sensível de responsabilidades, etc
* Habilidades de comunicação
* Arquitetura
* Observabilidade
* Implantabilidade
* Testes automatizados
* Retornos de feedback

Vou focar na refatoração. Quantas vezes você já ouviu a frase "precisamos refatorar isso"? Provavelmente dita para uma pessoa desenvolvedora em seu primeiro dia de programação sem pensar duas vezes.

De onde essa frase vem? Por que refatorar é diferente de escrever código?

Sei que eu e muitas outras pessoas só *pensaram* que estavam refatorando, mas estávamos cometendo um erro.

[Martin Fowler descreve como as pessoas entendem a refatoração errada aqui.](https://martinfowler.com/bliki/RefactoringMalapropism.html)

> No entanto, o termo "refatoração" costuma ser utilizado de forma inapropriada. Se alguém fala que um sistema ficará quebrado por alguns dias enquanto está sendo refatorado, pode ter certeza que eles não estão refatorando.

Então o que é refatoração?

### Fatoração

Quando estudava matemática na escola, você provavelmente aprendeu fatoração. Aqui vai um exemplo bem simples:

* Calcule `1/2 + 1/4`

Para fazer isso você `fatora` os denominadores (você também pode conhecer como MMC, mínimo múltiplo comum), transformando a expressão em `2/4 + 1/4` que então pode se transformar em `3/4`.

Podemos tirar algumas lições importantes disso. Quando `fatoramos a expressão`, `não mudamos o que ela faz`. Ambas as expressões são iguais a `3/4`, mas facilitamos a forma como trabalhamos com esse resultado; trocar `1/2` por `2/4` torna nosso "domínio" mais fácil.

Quando refatora seu código, você tenta encontrar formas de tornar seu código mais fácil de entender e "encaixar" no seu entendimento atual do que o sistema precisa fazer. Mas é extremamente importante que **o comportamento do código não seja alterado**.

#### Exemplo em Go

Aqui está uma função que cumprimenta `nome` em uma `linguagem` específica:

```go
func Ola(nome, linguagem string) string {

  if linguagem == "br" {
     return "Olá, " + nome
  }

  if linguagem == "fr" {
     return "Bonjour, " + nome
  }

  // e mais várias linguagens

  return "Hello, " + nome
}
```

Não é bom ter várias condicionais `if` e temos uma duplicação que concatena um cumprimento específico da linguagem com `,` e o `nome`. Logo, vou refatorar o código.

```go
func Ola(nome, linguagem string) string {
      return fmt.Sprintf(
          "%s, %s",
          cumprimento(linguagem),
          nome,
      )
}

var cumprimentos = map[string]string {
  br: "Olá",
  fr: "Bonjour",
  // etc..
}

func cumprimento(linguagem string) string {
  cumprimento, existe := cumprimentos[linguagem]

  if existe {
     return cumprimento
  }

  return "Hello"
}
```

A natureza dessa refatoração não é tão importante. O que importa é que não mudei o comportamento do código.

Quando estiver refatorando, você pode fazer o que quiser: adicionar interfaces, tipos novos, funções, métodos etc. A única regra é que você não mude o comportamento do software.

### Quando estiver refatorando o código, seu comportamento não deve ser modificado

Isso é muito importante. Se estiver mudando o comportamento enquanto refatora, você vai estar fazendo *duas* coisas de uma vez. Como engenheiros de software, aprendemos a dividir o sistema em diferentes arquivos/pacotes/funções/etc porque sabemos que tentar entender algo enorme e acoplado é difícil.

Não queremos ter que pensar sobre muitas coisas ao mesmo tempo porque é aí que cometemos erros. Já vi tantos esforços de refatoração falharem pelas pessoas que estavam desenvolvendo darem um passo maior que a perna.

Quando fazia fatorações nas aulas de matemática com papel e caneta, eu precisava verificar manualmente que não havia mudado o significado das expressões na minha cabeça. Como sabemos que não estamos mudando o comportamento quando refatoramos as coisas no código, especialmente em um sistema que não é tão simples?

As pessoas que escolhem não escrever testes vão depender do teste manual. Para quem não trabalha em um projeto pequeno, isso vai ser uma tremenda perda de tempo e não vai escalar a longo prazo.

**Para ter uma refatoração segura, você precisa escrever testes unitários**, porque eles te dão:

* Confiança de que você pode mudar o código sem se preocupar com mudar seu comportamento
* Documentação para humanos sobre como o sistema deve se comportar
* Feedback mais rápido e confiável que o teste manual

#### Exemplo em Go

Um teste unitário para a nossa função `Ola` pode ser feito assim:

```go
func TestOla(t *testing.T) {
  obtido := Ola(“Chris”, br)
  esperado := "Olá, Chris"

  if obtido != esperado {
     t.Errorf("obtido '%s' esperado '%s'", obtido, esperado)
  }
}
```

Na linha de comando, posso executar `go test` e obter feedback imediato se minha refatoração alterou o comportamento da função. Na prática, é melhor aprender onde fica o botão mágico que vai executar seus testes dentro do seu editor/IDE (ou rodar os testes sempre que salvar o arquivo).

Você deve entrar em uma rotina em que acaba fazendo:

* Refatorar uma parte pequena
* Executar testes
* Repetir

Tudo dentro de um ciclo de feedback contínuo para que você não caia em uma cilada e cometa erros.

Ter um projeto onde os seus principais comportamentos são testados unicamente e te dão feedback em menos de um segundo traz uma relação forte de segurança para refatorar sempre que for necessário. Isso nos ajuda a gerenciar a complexidade crescente que Lehman descreve.

## Se testes unitários são tão bons, por que há resistência em escrevê-los?

De um lado, é possível ver pessoas (como eu) dizendo que testes unitários são importantes para a saúde do seu sistema a longo prazo, porque eles certificam que você possa continuar refatorando com confiança.

Do outro lado, é possível ver pessoas descrevendo experiências com testes unitários que na verdade *dificultaram* a refatoração.

Se pergunte o seguinte: com qual frequência você precisa mudar seus testes quando refatora? Estive em diversos projetos com boa cobertura de testes e mesmo assim os engenheiros estavam relutantes em refatorar por causa do esforço perceptível de alterar testes.

Esse é o oposto do que prometemos!

### Por que isso acontece?

Imagine que te pediram para desenvolver um quadrado e você chegou à conclusão que seria necessário unir dois triângulos.

![Dois triângulos retângulos formando um quadrado](https://i.imgur.com/ela7SVf.jpg)

Escrevemos nossos testes unitários nos baseando no nosso quadrado para ter certeza de que os lados são iguais e depois escrevemos alguns testes em relação aos nossos triângulos. Queremos ter certeza de que nossos triângulos são renderizados corretamente, então afirmamos que os ângulos somados dos triângulos dão 180 graus, ou verificamos que os dois são criados, etc etc. A cobertura de testes é muito importante e escrever esses testes é bem fácil, então por que não?

Algumas semanas depois, a Lei da Mudança Contínua bate no seu sistema e uma nova pessoa desenvolvedora faz algumas mudanças. Ela acredita que seria melhor se os quadrados fossem formados por dois retângulos ao invés dos dois triângulos.

![Dois retângulos formando um quadrado](https://i.imgur.com/1G6rYqD.jpg)

Ela tenta fazer essa refatoração e percebe que alguns testes falharam. Ela quebrou algum comportamento realmente importante aqui? Agora ela tem que investigar esses testes de triângulo e entender o que está acontecendo.

*Na verdade, não é tão importante que o quadrado seja formado por triângulo*, mas **nossos testes fizeram com que isso parecesse mais importante do que deveria em relação aos detalhes da nossa implementação**

## Favorecer o comportamento do teste ao invés do detalhe da implementação

Quando ouço pessoas reclamando sobre testes unitários, frequentemente o motivo é que eles estão em um nível errado de abstração. Eles testam detalhes da implementação, testando coisas muito específicas ou fazendo muitos mocks.

Acredito que isso deriva de uma falta de entendimento do que testes unitários são e perseguem métricas vaidosas (cobertura de testes).

Se estou apenas testando o comportamento, não deveríamos apenas escrever testes de sistema/caixa preta? Esses tipos de testes geram muito valor em termos de verificar as principais jornadas do usuário, mas costumam ser difíceis de escrever e lentos para rodar. Por esse motivo, eles não são muito úteis para a *refatoração* porque o ciclo de feedback é lento. Além disso, os testes de caixa preta tendem a não te ajudar muito com as causas de origem comparados aos testes unitários.

Logo, *qual* é o nível de abstração correto?

## Escrever testes unitários de forma efetiva é um problema de design

Deixando testes de lado por um momento, é desejável "unidades" independentes e desacopladas dentro do seu sistema, centradas em torno de conceitos essenciais do seu domínio.

Gosto de imaginar essas unidades tão simples quanto blocos de Lego que têm APIs coerentes e que eu possa combinar com outros blocos para criar sistemas maiores. Por baixo dessas APIs pode haver várias coisas (tipos, funções etc) colaborando para fazê-las funcionar conforme esperado.

Por exemplo: se estiver escrevendo um banco em Go, você deve ter um pacote "conta". Ele vai te apresentar uma API que não vaza detalhes da implementação e é fácil de ser integrado.

Se tiver essas unidades que seguem essas propriedades, você consegue escrever testes unitários para suas APIs públicas. *Por definição*, esses testes só podem testar os comportamentos importantes. Por baixo dos panos dessas unidades, fico livre para refatorar a implementação o quanto eu precisar e os testes para a maior parte dela não devem me atrapalhar.

### Mas são testes unitários, mesmo?

**SIM**. Testes unitários são feitos para "unidades", como já descrevi. Eles *nunca* devem ser feitos para uma classe/função/seja lá o que for.

## Conclusão

Falamos sobre

* Refatoração
* Testes unitários
* Desenvolvimento de unidade

O que podemos começar a ver é que essas facetas do desenvolvimento de software reforçam uma à outra.

### Refatoração

* Nos dá sinais sobre nossos testes unitários. Se precisamos fazer validações manuais, precisamos de mais testes. Se testes estão falhando incorretamente, então nossos testes estão no nível errado de abstração (ou não têm valor e precisam ser deletados).
* Nos ajuda a lidar com as complexidades dentro e entre nossas unidades.

### Testes unitários

* Nos dão a garantia para refatoração.
* Verificam e documentam o comportamento de nossas unidades.

### Unidades (bem definidas)

* Facilitam a escrita de testes unitários *significativos*.
* Facilitam a refatoração.

Há um processo que nos ajuda a alcançar um ponto onde podemos refatorar nosso código para lidar com a complexidade e manter nossos sistemas maleáveis?

## Por que Desenvolvimento Orientado a Testes (TDD)

Algumas pessoas levam as citações de Lehman sobre como o software deve mudar a sério demais e elaboram sistemas complexos demais, gastando muito tempo tentando prever o impossível para criar o sistema extensível "perfeito" e acabam entendendo da forma errada e chegando a lugar nenhum.

Isso vem da época das trevas do software onde um time de analistas costumava perder seis meses escrevendo um documento de requerimentos e a equipe de arquitetura perdia outros seis meses para desenvolvê-lo e alguns anos depois o projeto inteiro falhava.

Eu disse que era uma época das trevas, mas isso ainda acontece!

O movimento ágil nos ensina que precisamos trabalhar de forma iterativa, começando com pouca coisa e evoluindo o software para que tenhamos retorno rápido do design do nosso software e como ele trabalha com usuários reais; o TDD reforça essa abordagem.

O TDD aborda as leis citadas por Lehman e outras lições difíceis aprendidas no decorrer da história encorajando uma metodologia de refatoração constante e entrega contínua.

### Etapas pequenas

* Escrever um teste pequeno para uma unidade do comportamento desejado
* Verificar que o teste falha com um erro claro (vermelho)
* Escrever o mínimo de código para fazer o teste passar (verde)
* Refatorar (azul)
* Repetir

Conforme você pratica, essa mentalidade vai se tornar natural e rápida.

Você vai esperar que esse ciclo de feedback não leve muito tempo e se sentir desconfortável se estiver em um estado em que seu sistema não está "verde", já que isso pode indicar que você pode ter deixado algo passar.

Você sempre vai desenvolver de forma a criar funcionalidades pequenas & úteis confortavelmente reforçadas pelo feedback dos seus testes.

## Resumindo

* O ponto forte do software é que podemos mudá-lo. A *maioria* dos softwares requer mudança com o tempo de formas imprevisíveis; não tente pensar muito à frente porque é difícil prever o futuro.
* Ao invés disso, precisamos criar nosso software de forma que ele possa se manter maleável. Para mudar o software precisamos refatorá-lo conforme ele evolui, ou vai acabar virando uma bagunça.
* Um bom conjunto de testes pode te ajudar a refatorar mais rápido e de forma menos estressante.
* Escrever bons testes unitários é um problema de design. Logo, pense em estruturar seu código de forma que ele tenha unidades significativas que possam ser unidas como blocos de Lego.
* O TDD pode ajudar e te forçar a desenvolver softwares bem fatorados continuamente, reforçados por testes para te ajudar com futuros trabalhos que podem chegar.


# Como contribuir

Contribuições são mais que bem vindas. Espero que esse se torne um ótimo lar para guias sobre como aprender Go escrevendo testes. Você pode submeter uma PR ou criar uma issue [aqui](https://github.com/larien/aprenda-go-com-testes).

## O que estamos buscando

* Ensinar funcionalidades de Go (conceitos como `if`, `select`, estruturas, métodos etc).
* Demonstrar as funcionalidades interessantes dentro da biblioteca padrão e o quão fácil é utilizar o TDD para criar um servidor HTTP, por exemplo.
* Mostrar como o ferramental do Go, incluindo benchmarking, race detectors, etc pode te ajudar a construir um ótimo software.

Se não se sentir confiante em enviar seu próprio guia, criar uma issue para algo que queira aprender também é uma contribuição válida.

## Estilo a ser seguido

* Sempre reforce o ciclo TDD. Dê uma olhada no [Exemplo de Capítulo](/aprenda-go-com-testes/meta/exemplo).
* Dê ênfase em iterar sobre funcionalidades orientando-se por testes. O exemplo [Olá, mundo](/aprenda-go-com-testes/primeiros-passos-com-go/ola-mundo) funciona bem porque aos poucos tornamos o código mais sofisticado e aprendemos novas técnicas *orientadas por testes*. Por exemplo:
  * `Hello()` <- como escrever funções e retornar tipos.
  * `Hello(name string)` <- argumentos, constantes.
  * `Hello(name string)` <- padrão para "mundo" usando `if`.
  * `Hello(name, language string)` <- `switch`.
* Tente diminuir a barreira de conhecimento com explicações claras e simples.
  * É importante pensar em exemplos que demonstrem o que você está tentando ensinar sem confundir a leitura com outras funcionalidades.
  * Por exemplo: você pode aprender `structs` sem entender ponteiros.
  * Seja breve.
* Siga o [guia de estilo para Comentários de Revisão de Código](https://github.com/golang/go/wiki/CodeReviewComments). É importante ter um estilo consistente em todas as seções.
* Sua seção deve ter uma aplicação executável no final (como um `package main` com uma função `main`) para que as pessoas possam vê-la em ação e brincar com ela.
* Todos os testes devem passar.
* Execute o `/build.sh` antes de subir uma PR.


# Como traduzir

Requisitos: [Git](https://git-scm.com/downloads) instalado!

## Traduções x Revisões

É importante diferenciarmos quando estamos traduzindo de quando estamos revisando. As traduções acontecem diretamente relacionadas ao conteúdo original, escrito em inglês. Elas passaram por duas aprovações de outras pessoas envolvidas no projeto e já foram aceitas junto ao conteúdo oficial. As revisões acontecem quando alguma melhoria pode ser feita na tradução e passam por somente uma aprovação de outra pessoa envolvida no projeto.

## Começando a brincadeira

Na aba Projetos você pode encontrar todas as Issues criadas de acordo com cada tópico discutido no livro. Tendo selecionado um tópico para ser traduzido/revisado:

* Dê assign na issue para o seu apelido
* Clone o repositório:

```bash
git clone https://github.com/larien/aprenda-go-com-testes.git
```

* Crie uma branch seguindo o padrão `traducao/<nome-do-topico>` ou `revisao/<nome-do-topico>`:

```bash
git checkout -b traducao/<nome-do-topico>
```

* Você pode salvar a sua tradução aos poucos com `commits`:

```bash
git commit -m "Pequena descrição do que foi traduzido aqui"
```

* E não se esqueça de subir sua tradução para o repositório remoto!

```bash
git push -u origin traducao/<nome-do-topico>
```

E que comecem as traduções! :))

## Pontos para prestar atenção

* Como pode ter percebido, não é um livro formal. Logo, não é necessário ter uma linguagem rebuscada: tente usar termos e palavras acessíveis para quem está conhecendo a linguagem.
* Normalmente o artigo está relacionado a algum código presente no repositório; lembre-se de traduzir também os comentários e documentação relacionados.
* Se for um termo muito técnico, não custa nada colocar uma breve explicação do que ele é ou linkar para uma referência externa.
* Lembre-se de utilizar uma linguagem neutra! Se alguma parte do texto especificar algum gênero, reescreva-o para que inclua todo mundo.
* Se houver dúvida em alguma palavra ou tradução, coloque um asterico (\*) que o pessoal que for revisar vai tentar te ajudar.
* Quando for analisar a submissão de alguma tradução ou revisão, lembre-se de ter a gentileza em primeiro lugar! Todas as pessoas que colaborarem com o projeto querem ter um conteúdo de qualidade na nossa língua e, acima de tudo, querem aprender. Não se esqueça disso :)
* Leia o [contribuindo.md](/aprenda-go-com-testes/meta/contribuindo) :)

## Submetendo sua tradução/revisão

Após fazer sua tradução para a sua própria branch e subi-la para o GitHub:

* Vá na aba `Pull Requests` e clique em `New pull request`.
* Redirecione a sua branch para a branch `master` do projeto `larien/aprenda-go-com-testes`, se isso já não estiver definido.
* Na descrição, lembre-se de linkar a Issue referente ao tópico que você traduziu. Por exemplo, se o tópico que você selecionou é a Issue de número #14, digite na descrição do pull request `closes #14`. Isso vai automaticamente mover o card da sua Issue para `Done`.
* Se for uma revisão, não é necessário linkar nenhuma issue.
* Clique em `Create pull request`
* Certifique-se que sua branch passe na avaliação do Travis CI (muito daora esse carinha aí, né?)
* Aguarde a aprovação das outras pessoas envolvidas no projeto. Você receberá a notificação por e-mail quando sua PR for aprovada :) seu nome também irá para a lista de contribuições na página inicial.


# Exemplo de capítulo

Introdução

## Escreva o teste primeiro

## Execute o teste

## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado

## Escreva código o suficiente para fazer o teste passar

## Refatoração

## Repetir a cada novo requisito

## Resumo


# Glossário

Sinta-se livre para colaborar com esse glossário no repositório oficicial. Organizar termos do glossário em ordem alfabética. Se possível, procurar pelos termos nos textos já traduzidos e linkar com esse glossário.

## palavra

Explicação.

Encontrado em:

* [capitulo](https://github.com/larien/learn-go-with-tests/tree/b841d39645f10f92f129e35b21e9bb1e0ebe6d9c/outros/link-para-o-capitulo.md)

## palavra

Explicação.

Encontrado em:

* [capitulo](https://github.com/larien/learn-go-with-tests/tree/b841d39645f10f92f129e35b21e9bb1e0ebe6d9c/outros/link-para-o-capitulo.md)

## palavra

Explicação.

Encontrado em:

* [capitulo](https://github.com/larien/learn-go-with-tests/tree/b841d39645f10f92f129e35b21e9bb1e0ebe6d9c/outros/link-para-o-capitulo.md)


