GET /jogadores/{nome}
deve retornar um número indicando o número total de vitóriasPOST /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
). Faça o teste passar rapidamente, cometendo quaisquer pecados necessários nesse processo.
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.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.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.Handler
, usada para receber a requisição e avaliar o que fazer com os dados recebidos.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"
.Request
) para enviar ao servidor, e então queremos inspecionar o que o nosso Tratador escreve para o ResponseWriter
.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../servidor_test.go:13:2: undefined: ServidorJogador
servidor.go
, e nele a função ServidorJogador
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.main.go
para nossa aplicação, com o código abaixo.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
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.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.
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.Certamente precisamos de algum tipo de armazenamento para controlar qual jogador recebe qual pontuação. É estranho que os valores sejam predefinidos em nossos testes.
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.ServidorJogador
separando a parte de obtenção da pontuação em uma função.ObterPontuacaoJogador
. Isso parece ser o lugar correto para isolar as responsabilidades usando interfaces.servidor.go
, a função que refatoramos para ser uma interface: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
).Handler
) adicionando um método à nossa nova estrutura ServidorJogador
e adicionado neste método o código existente.armazenamento.ObterPontuacaoJogador
para obter a pontuação, ao invés da função local definida anteriormente (e que podemos remover).servidor.go
):./main.go:9:58: type ServidorJogador is not an expression
ServidorJogador
e então chamar o método ServeHTTP
.main.go
não vai compilar pelas mesmas razões.ArmazenamentoJogador
em nossos testes. Precisamos fazer, no arquivo servidor_test.go
um código de esboço para nos ajudar.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
.ArmazenamentoJogador
, quando você o usar com um ServidorJogador
você deve obter as respostas definidas.http://localhost:5000/jogadores/Maria
.ArmazenamentoJogador
.main.go
: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.POST
em /jogadores/{nome}
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.StatusNotFound
em todas as respostas, mas todos os nossos testes estão passando!StatusOK
quando jogadores existem em nosso armazenamento.status
(código de retorno HTTP) em todos os nossos testes, por isso existe a função auxiliar verificarRespostaCodigoStatus
para ajudar com isso.ServidorJogador
para que retorne não encontrado (HTTP status 404) se a pontuação for 0.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.if
para identificar o método da requisição vai resolver o problema.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
.POST
a /jogadores/{nome}
, nosso ArmazenamentoJogador
registra a vitória.EsbocoArmazenamentoJogador
com um novo método GravarVitoria
e então inspecionar as chamadas.EsbocoArmazenamentoJogador
RegistrarVitoria
, precisamos atualizar a definição de ArmazenamentoJogador
para que o ServidorJogador
funcione como esperado.main
não compila maisArmazenamentoJogadorEmMemoria
, adicionando esse método.ArmazenamentoJogador
tem o método GravarVitoria
, podemos chamar de dentro do nosso ServidorJogador
"Marcela"
não é bem o que queremos enviar para RegistrarVitoria
, então vamos ajustar os testes.registrosVitorias
, e então podemos acessar, sem erros, o primeiro elemento e verificar se é igual a jogador
.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.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.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).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.ArmazenamentoJogadorEmMemoria
e ServidorJogador
.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.resposta
) porque vamos obter a pontuação do jogador
.ArmazenamentoJogadorEmMemoria
).ArmazenamentoJogadorEmMemoria
, que nos ajudariam a encontrar a solução.