/liga
. Durante o caminho aprendemos como lidar com JSON, tipos embutidos e roteamento./liga
que deveria retornar os jogadores ordenados pelo número de vitórias!GuardarJogadores
que nós usamos.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
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.GuardaJogador
, então escreveremos testes para nossa armazenamento chamando os métodos que nós precisamos implementar. Começaremos com PegaLiga
.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
.SistemaDeArquivoDeArmazenamentoDoJogador
em um novo arquivoReader
mas não está esperando um e não tem PegaLiga
definida ainda.liga.go
e coloque isso nele.obterLigaDaResposta
in serv_test.go
io.Reader
é definida.ler
uma segunda vez?Reader
chegou no final, então não tem mais nada para ser lido. Precisamos de um jeito de avisar para voltar ao inicio.SistemaDeArquivoDeArmazenamentoDoJogador
para pegar essa interface no lugar?string.NewReader
que nós usamos em nosso teste também implementa ReadSeeker
então não precisamos mudar nada.PegarPontuacaooDoJogador
../SistemaDeArquivoDeArmazenamentoDoJogador_test.go:38:15: armazenamento. undefined (type SistemaDeArquivoDeArmazenamentoDoJogador has no field or method )
SalvaVitoria
.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.strings.Reader
não implementa ReadWriteSeeker
, então o que vamos fazer?*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.strings.Reader
com um os.File
."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.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()
../SistemaDeArquivoDeArmazenamentoDoJogador_test.go:67:8: armazenamento.SalvaVitoria undefined (type SistemaDeArquivoDeArmazenamentoDoJogador has no field or method SalvaVitoria)
liga[i].Vitorias++
invés de jogador.Vitorias++
.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.PegaPontuacaoDoJogador
e SalvaVitoria
, estamos iterando sobre []Jogador
para encontrar um jogador pelo nome.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.liga.go
adicionamos o seguinteLiga
facilmente será encontrado um dado jogador.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
.SistemaDeArquivoDeArmazenamentoDoJogador
.Liga
podem ser refatoradas.Find
returna nil
por não ter conseguido encontrar o jogador.armazenamento
no teste de integração. Isto nos dará mais confiança que o software funciona e então podemos deletar o redundante NovoArmazenamentoDeJogadorNaMemoria
.TestRecordingWinsAndRetrievingThem
substitui a velha armazenamento.NovoArmazenamentoDeJogadorNaMemoria
. main.go
terá problemas de compilação que nos motivará para agora usar nossa nova armazenamento no código "real".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.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.SistemaDeArquivoDeArmazenamentoDoJogador
para ser usado nas leitura então.f.liga
no lugar.SistemaDeArquivoDeArmazenamentoDoJogador
então fixe-o chamando nosso construtor.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?Fita
. Criamos um novo arquivo com o seguinteWrite
agora, já que encapsula a parte de Procura
. Isso que dizer que SistemaDeArquivoDeArmazenamentoDoJogador
pode ter uma referência a Writer
invés disso.fita
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.fita_test.go
: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.fita
para o seguinteio.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.TestaFita_Escrita
deve estar passando!SalvaVitoria
temos uma linhajson.NewEncoder(f.bancoDeDados).Encode(f.league)
.Encoder
para nosso tipo.SalvaVitoria
.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.Truncate
que também está no *os.File
. Isso seria uma opção para criar nossa própria interface pegando esses requisitos.*os.File
então não precisamos do polimorfismo que interface nos dá.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.