A unit test in Go is a function that automatically checks a small piece of code.
Go includes testing support in the standard library. You don’t need to install a framework to get started, although we can add external utilities if the project requires them.
Let’s look at the idiomatic way, starting with a simple function.
Code to test
Suppose we have this file calculadora.go:
package calculadora
func Sumar(a, b int) int {
return a + b
}We want to check that Sumar does what it says. The example is small, but it allows us to focus on the conventions of the testing package.
Create the test file
Tests live in files ending with _test.go.
We create calculadora_test.go:
package calculadora
import "testing"
func TestSumar(t *testing.T) {
resultado := Sumar(2, 3)
esperado := 5
if resultado != esperado {
t.Errorf("Sumar(2, 3) = %d; esperado %d", resultado, esperado)
}
}There are three important conventions:
- The file ends with
_test.go. - The function starts with
Test. - The function receives
t *testing.T.
Run the tests
From the module folder:
go testIf everything goes well, you’ll see something like:
PASS
ok ejemplo/calculadora 0.003sTo run all packages in the project:
go test ./...That ./... means: this package and all subpackages.
Failing a test
If we change the expected value:
esperado := 6The test will fail:
--- FAIL: TestSumar (0.00s)
calculadora_test.go:10: Sumar(2, 3) = 5; esperado 6
FAILThe message should explain what happened. A test that fails with a clear message is much more useful than one that just yells “wrong”.
t.Fatal and t.Error
t.Error marks the test as failed but continues executing the function.
t.Fatal marks the test as failed and stops that test immediately.
func TestDividir(t *testing.T) {
resultado, err := Dividir(10, 2)
if err != nil {
t.Fatalf("no se esperaba error: %v", err)
}
if resultado != 5 {
t.Errorf("resultado = %d; esperado 5", resultado)
}
}We use Fatal when continuing makes no sense. For example, if we don’t have a valid result because the function returned an error.
Tests in the same package or in an external one
We can write tests in the same package:
package calculadoraOr in an external package:
package calculadora_testThe second option forces testing the package as someone would use it from the outside, only with its exported API. It is stricter and usually works well for behavior tests. Tests in the same package, on the other hand, can access non-exported details.
Coverage and race detector
We can combine full execution with coverage and race detection:
go test -cover ./...
go test -race ./...Coverage indicates which code the tests executed, not whether the checks are good. The race detector only finds conflicts that occur in the paths executed during the test.