Escrevendo testes unitários

Testes unitários nos ajudam muito a tentar manter a quantidade de bugs baixa, já que podemos testar várias hipóteses de forma automática. E por ser automatizada, a cada mudança que fazemos, os testes unitários também nos ajudam a garantir que não quebramos o sistema em partes que nem tocamos mas que dependem das mudanças que fizemos.

Embora exista alguns packages que ajudam na escrita dos testes unitários, a recomendação do time de desenvolvimento da linguagem é que os testes sejam escritos usando o package nativo da linguagem, pelo simples fato de que um package externo pode adicionar algum bug ou realizar algum teste de forma incorreta.

Antes de começar a escrever os testes, vamos criar um arquivo chamado soma.go e escrever uma pequena função que soma N valores.

package soma


func Soma(valores ...int) (total int) {
    for _, valor := range valores {
        total += valor
    }

    return
}

Feito isso, agora precisamos criar o arquivo para testar essa função. Por convenção, os arquivos de teste são criados no mesmo local onde o arquivo que ele está testando, e nome do arquivo tem o sufixo _test adicionado ao nome do arquivo onde estão as funções que ele testa.

No nosso caso, vamos criar o soma_test.go e criar a função TestSoma.

package soma


import "testing"


func TestSoma(t *testing.T) {
    test := []int{1, 2, 3, 4}
    total := Soma(test...)

    if total != 10 {
        t.Fatalf("Valor esperado: 10 - Valor retornado: %d", total)
    }
}

Feito isso, tudo que precisamos fazer agora é executar o comando abaixo:

$ go test ./...

O output deverá ser algo similar a isso:

ok      github.com/aprendagolang/soma   0.011s

Simples não?! Porém nós só estamos testando uma hipótese, o que praticamente não testa nossa função de verdade.

Para melhorar isso, vamos fazer uma pequena mudança na nossa função de teste para que possamos testar várias hipóteses, aumentando a garantia de que a função não contém bugs.

package soma

import "testing"

func TestSoma(t *testing.T) {
    testes := []struct {
        Valores   []int
        Resultado int
    }{
        {Valores: []int{1, 2, 3}, Resultado: 6},
	{Valores: []int{1, 2, 3, 4}, Resultado: 10},
	{Valores: []int{3, 3, 3, 3}, Resultado: 12},
	{Valores: []int{1, 1, 1, 1}, Resultado: 4},
	{Valores: []int{12, 20, 35}, Resultado: 67},
	{Valores: []int{19, 21, 32}, Resultado: 72},
    }

    for _, teste := range testes {
	total := Soma(teste.Valores...)
	if total != teste.Resultado {
	    t.Fatalf("Valor esperado: %d - Valor retornado: %d", teste.Resultado, total)
	}
    }
}

Ao invés de ficar duplicando, triplicando, quadru… você entendeu… as linhas que tínhamos escrito inicialmente, criamos uma struct dentro da função, onde passamos os Valores a serem testados e o Resultado esperado. Dessa forma fica muito mais simples adicionar novas hipóteses que queremos testar, como por exemplo, o que acontece se um dos valores passados for nill??? Vamos adicionar essa hipótese abaixo.

package soma

import "testing"

func TestSoma(t *testing.T) {
    comNill := make([]int, 3)
    comNill[0] = 10
    comNill[1] = 10

    testes := []struct {
        Valores   []int
        Resultado int
    }{
        {Valores: []int{1, 2, 3}, Resultado: 6},
	{Valores: []int{1, 2, 3, 4}, Resultado: 10},
	{Valores: []int{3, 3, 3, 3}, Resultado: 12},
	{Valores: []int{1, 1, 1, 1}, Resultado: 4},
	{Valores: []int{12, 20, 35}, Resultado: 67},
	{Valores: []int{19, 21, 32}, Resultado: 72},
	{Valores: comNill, Resultado: 20},
    }

    for _, teste := range testes {
	total := Soma(teste.Valores...)
	if total != teste.Resultado {
	    t.Fatalf("Valor esperado: %d - Valor retornado: %d", teste.Resultado, total)
	}
    }
}

Executando o go test ./... podemos ver que tudo continua funcionando normal.

Uma última mudança que podemos fazer, é adicionar o sufixo _test ao nome do package dentro do arquivo soma_test.go. Dessa forma, vamos testar a função como se ela estivesse sendo utilizada fora do package no qual ela foi criada.

Após essa mudança, precisamos fazer o import do package e mudar a chamada da função.

package soma_test

import (
    "testing"

    "github.com/aprendagolang/soma"
)

func TestSoma(t *testing.T) {
    comNill := make([]int, 3)
    comNill[0] = 10
    comNill[1] = 10

    testes := []struct {
        Valores   []int
        Resultado int
    }{
        {Valores: []int{1, 2, 3}, Resultado: 6},
	{Valores: []int{1, 2, 3, 4}, Resultado: 10},
	{Valores: []int{3, 3, 3, 3}, Resultado: 12},
	{Valores: []int{1, 1, 1, 1}, Resultado: 4},
	{Valores: []int{12, 20, 35}, Resultado: 67},
	{Valores: []int{19, 21, 32}, Resultado: 72},
	{Valores: comNill, Resultado: 20},
    }

    for _, teste := range testes {
	total := soma.Soma(teste.Valores...)
	if total != teste.Resultado {
	    t.Fatalf("Valor esperado: %d - Valor retornado: %d", teste.Resultado, total)
	}
    }
}

Espero que tenham gostado, e se ficou alguma dúvida, deixe nos comentários.

Até a próxima!


Subscreva

Fique por dentro de tudo o que acontece no mundo Go.

Deixe uma resposta