Por que você deveria escrever testes?

Programar não é uma tarefa fácil, escrever testes também não, mas parte do processo de desenvolvimento de software é escrever testes. Os testes do nosso código são uma boa maneira de garantir a qualidade e melhorar a confiabilidade, Introducing Go, 2016. Quando falamos em testes, estamos nos referindo implicitamente a testes automatizados, é a prática de escrever pequenos programas que verificam se o código se comporta conforme o esperado para determinadas entradas, The Go Programming Language, 2016.

Como são escritos testes em Golang?

O pacote testing da biblioteca padrão de Golang, provê suporte a escrita testes automatizados. Um arquivo com o sufixo _test.go deve existir, por exemplo, para testar sum.go, é criado um arquivo sum_test.go.

Dentro deste arquivo com sufixo _test.go são definidas as funções de teste, com a assinatura func TestFuncName(t *testing.T), onde o parametro t é uma referência ao tipo testing.T, importado do pacote testing, o prefixo Test é um requerimento no nome da função, para testar a função Sum(x, y) result do pacote math, podemos criar uma função de teste com a seguinte assinatura, func TestSum(t *testing.T).

func TestSum(t *testing.T) {
    x := 1
    y := 2
    expected := 3
    result := math.Sum(x, y)

    if result != expected {
        t.Errorf(
            "Expected that %d+%d is %d, but actual take %d",
            x, y, expected, result)
    }
}

Em Go, é comum o uso de uma tabela de testes para definir casos de testes, uma struct é criada, abstraindo todas as entradas e a saída esperada, por exemplo no teste de soma, teriam dois números a serem somados, e o resultado esperado da soma.

Ao invés de chamar a função soma, uma única vez, e validar seu resultado, com o uso de tabela de testes, um laço é definido para percorrer toda a tabela, chamando diversas vezes a função.

func TestSum(t *testing.T) {
    testTable := []struct {
        x int
        y int
        expected int
    }{
        {1, 1, 2},
        {0, 1, 1},
        {0, 0, 0},
        {0, -1, -1},
        {-1, -1, -2},
    }

    for _, test := range testTable {
        result := math.Sum(test.x, text.y)
        if result != tabela.expected {
            t.Errorf(
                "Expected that %d+%d is %d, but actual take %d",
                test.x, test.y, test.expected, result)
        }
    }
}

AVISO: Go é bem restrito quanto ao nomear um pacote, a única excessão para nomes compostos é o sufixo _test, como em package math_test, dessa forma quem estiver usando o pacote math não terá visibilidade das funções de teste.

Quando tudo ocorreu conforme esperado, não houve nenhum erro, é dito que os testes passaram, por isso em Go, se não chamarmos nenhum método t.Fail() ou t.Error ou Errorf() para formatar o erro, se nenhuma destas funcões forem invocadas, os testes passarão.

Caso você prefira utilizar a linha de racíocio de asserções, existe um pacote chamado testify, que provê funções auxiliares em validações, e comparações.

Como rodar os testes em Golang?

A ferramenta de teste $ go test verifica todos os arquivos que terminam com _test.go em busca de funções de teste, gera um pacote principal temporário que o utiliza da maneira adequada, executa os testes, relata os resultados e, em seguida, os limpa, The Go Programming Language, 2016.

$ go test ./...

?   	example.com/test	[no test files]
ok  	example.com/test/math	0.001s

Caso os testes não passem, uma mensagem FAIL será exibida, apresentando o nome da função de teste de falhou, e caso tenha sido implementado, alguma mensagem de erro, explicando os motivos do erro, ela também vai aparecer no na saída do terminal stdout.

$ go test ./...

--- FAIL: TestSum (0.00s)
    sum_test.go:25: Expected that 1+1 is 2, but actual take 4
    sum_test.go:25: Expected that 0+1 is 1, but actual take 3
    sum_test.go:25: Expected that 0+0 is 0, but actual take 2
    sum_test.go:25: Expected that 0+-1 is -1, but actual take 1
    sum_test.go:25: Expected that -1+-1 is -2, but actual take 0
FAIL
FAIL	example.com/test/math	0.001s
FAIL

Conclusões!

Utilizar tabela de testes é mais complexo do que escrever várias funções de testes para cobrir os possíveis cenários, mas diminui a quantidade de código.

Não é necessário nenhum pacote externo à biblioteca padrão para escrever testes em Golang, o pacote testing provê o necessário. O pacote testify pode ser usado em casos mais complexos, com mais validações, e para trabalhar melhor com formatações de mensagens de erro e logs.

Referências