Perimetro(largura float64, altura float64)
, onde float64
representa números em ponto flutuante como 123.45
.f
é para nosso float64
e o .2
significa imprimir duas casas decimais../formas_test.go:6:9: undefined: Perimetro
indefinido: Perimetro
formas_test.go:10: resultado 0, esperado 40
.Area(largura, altura float64)
que retorna a área de um retângulo.AreaDoRetangulo
. Uma solução mais limpa é definir nosso próprio tipo chamado Retangulo
que encapsula este conceito para nós.struct
assim:Retangulo
em vez de um simples float64
.struct
com a sintaxe minhaStruct.campo
.Retangulo
para a função mostra nossa intenção com mais clareza, mas existem mais benefícios em usar structs
que já vamos entender.Area
para círculos../formas_test.go:28:13: undefined: Circulo
Circulo
../formas_test.go:29:14: cannot use circulo (type Circulo) as type Retangulo in argument to Area
./formas.go:20:32: Area redeclared in this block
Area(Circulo)
em um novo pacote, só que isso parece um exagero aqui.t.Errorf
, nós chamamos o método Errorf
na instância de nosso t
(testing.T
).Area(retangulo)
, você só pode chamar métodos em "coisas" específicas.type Circulo has no field or method Area
func (nomeDoReceptor TipoDoReceptor) NomeDoMetodo(argumentos)
.nomeDoReceptor
. Em muitas outras linguagens de programação isto é feito implicitamente e você acessa o receptor através de this
.Area
de círculo passar, vamos emprestar a constante Pi
do pacote math
(lembre-se de importá-lo).Area()
e então verificar o resultado.verificaArea
que permita passar tanto Retangulo
quanto Circulo
, mas falhe ao compilar se tentarmos passar algo que não seja uma forma.Forma
seja passada. Se tentarmos chamá-la com algo que não seja uma forma, não vai compilar.Forma
usando uma declaração de interface.tipo
, assim como fizemos com Retangulo
e Circulo
, mas desta vez é uma interface
em vez de uma struct
.meu tipo Foo implementa a interface Bar
.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.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.structs
, podemos apresentar os "table driven tests" (testes orientados por tabela).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.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.{Triangulo{12, 6}, 36.0},
à nossa lista../formas_test.go:25:4: undefined: Triangulo
Triangulo
:Triangulo não implementa Forma (método Area faltando)
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:formas_test.go:31: resultado 0.00, esperado 36.00
MinhaStruct{valor1, valor2}
, mas você pode opcionalmente nomear esses campos.O teste é lido de forma mais clara, como se fosse uma afirmação da verdade, não uma sequência de operações
Triangulo
e tivemos um teste falhando? Ele imprimiu formas_test.go:31: resultado 0.00 esperado, 36.00
.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.%#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.esperado
para algo mais descritivo como temArea
.t.Run
e renomear os casos de teste.t.Run
você terá uma saída de testes mais limpa em caso de falhas, além de imprimir o nome do caso.go test -run TestArea/Retangulo
.