Recherche…
Introduction
Go est livré avec ses propres installations de test qui ont tout ce qu'il faut pour exécuter des tests et des tests de performances. Contrairement à la plupart des autres langages de programmation, il n’est souvent pas nécessaire de disposer d’un cadre de test distinct, bien que certains existent.
Test de base
main.go
:
package main
import (
"fmt"
)
func main() {
fmt.Println(Sum(4,5))
}
func Sum(a, b int) int {
return a + b
}
main_test.go
:
package main
import (
"testing"
)
// Test methods start with `Test`
func TestSum(t *testing.T) {
got := Sum(1, 2)
want := 3
if got != want {
t.Errorf("Sum(1, 2) == %d, want %d", got, want)
}
}
Pour exécuter le test, utilisez simplement la commande go test
:
$ go test
ok test_app 0.005s
Utilisez le drapeau -v
pour voir les résultats de chaque test:
$ go test -v
=== RUN TestSum
--- PASS: TestSum (0.00s)
PASS
ok _/tmp 0.000s
Utilisez le chemin d'accès ./...
pour tester les sous-répertoires de manière récursive:
$ go test -v ./...
ok github.com/me/project/dir1 0.008s
=== RUN TestSum
--- PASS: TestSum (0.00s)
PASS
ok github.com/me/project/dir2 0.008s
=== RUN TestDiff
--- PASS: TestDiff (0.00s)
PASS
Exécuter un test particulier:
S'il existe plusieurs tests et que vous souhaitez exécuter un test spécifique, vous pouvez le faire comme suit:
go test -v -run=<TestName> // will execute only test with this name
Exemple:
go test -v run=TestSum
Tests de benchmark
Si vous souhaitez mesurer les benchmarks, ajoutez une méthode de test comme celle-ci:
sum.go
:
package sum
// Sum calculates the sum of two integers
func Sum(a, b int) int {
return a + b
}
sum_test.go
:
package sum
import "testing"
func BenchmarkSum(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Sum(2, 3)
}
}
Ensuite, pour exécuter un benchmark simple:
$ go test -bench=.
BenchmarkSum-8 2000000000 0.49 ns/op
ok so/sum 1.027s
Tests unitaires pilotés par tableau
Ce type de test est une technique populaire pour tester avec des valeurs d'entrée et de sortie prédéfinies.
Créez un fichier appelé main.go
avec le contenu:
package main
import (
"fmt"
)
func main() {
fmt.Println(Sum(4, 5))
}
func Sum(a, b int) int {
return a + b
}
Après l'avoir exécuté avec, vous verrez que le résultat est 9
. Bien que la fonction Sum
semble assez simple, il est conseillé de tester votre code. Pour ce faire, nous créons un autre fichier nommé main_test.go
dans le même dossier que main.go
, contenant le code suivant:
package main
import (
"testing"
)
// Test methods start with Test
func TestSum(t *testing.T) {
// Note that the data variable is of type array of anonymous struct,
// which is very handy for writing table-driven unit tests.
data := []struct {
a, b, res int
}{
{1, 2, 3},
{0, 0, 0},
{1, -1, 0},
{2, 3, 5},
{1000, 234, 1234},
}
for _, d := range data {
if got := Sum(d.a, d.b); got != d.res {
t.Errorf("Sum(%d, %d) == %d, want %d", d.a, d.b, got, d.res)
}
}
}
Comme vous pouvez le constater, une partie des structures anonymes est créée, chacune avec un ensemble d’entrées et le résultat attendu. Cela permet de créer un grand nombre de cas de test tous au même endroit, puis de les exécuter en boucle, ce qui réduit la répétition du code et améliore la clarté.
Exemples de tests (tests d'auto-documentation)
Ce type de test s'assure que votre code compile correctement et apparaîtra dans la documentation générée pour votre projet. En plus de cela, les tests d'exemple peuvent affirmer que votre test produit une sortie correcte.
sum.go
:
package sum
// Sum calculates the sum of two integers
func Sum(a, b int) int {
return a + b
}
sum_test.go
:
package sum
import "fmt"
func ExampleSum() {
x := Sum(1, 2)
fmt.Println(x)
fmt.Println(Sum(-1, -1))
fmt.Println(Sum(0, 0))
// Output:
// 3
// -2
// 0
}
Pour exécuter votre test, exécutez go test
dans le dossier contenant ces fichiers ou placez ces deux fichiers dans un sous-dossier nommé sum
, puis dans le dossier parent, exécutez go test ./sum
. Dans les deux cas, vous obtiendrez une sortie similaire à celle-ci:
ok so/sum 0.005s
Si vous vous demandez comment cela teste votre code, voici un autre exemple de fonction, qui échoue en fait au test:
func ExampleSum_fail() {
x := Sum(1, 2)
fmt.Println(x)
// Output:
// 5
}
Lorsque vous exécutez go test
, vous obtenez la sortie suivante:
$ go test
--- FAIL: ExampleSum_fail (0.00s)
got:
3
want:
5
FAIL
exit status 1
FAIL so/sum 0.006s
Si vous voulez voir la documentation de votre paquet de sum
- lancez simplement:
go doc -http=:6060
et accédez à http: // localhost: 6060 / pkg / FOLDER / sum / , où FOLDER est le dossier contenant le package de sum
(dans cet exemple, so
). La documentation de la méthode sum ressemble à ceci:
Test des requêtes HTTP
main.go:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func fetchContent(url string) (string, error) {
res, err := http.Get(url)
if err != nil {
return "", nil
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", err
}
return string(body), nil
}
func main() {
url := "https://example.com/"
content, err := fetchContent(url)
if err != nil {
log.Fatal(err)
}
fmt.Println("Content:", content)
}
main_test.go:
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
)
func Test_fetchContent(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hello world")
}))
defer ts.Close()
content, err := fetchContent(ts.URL)
if err != nil {
t.Error(err)
}
want := "hello world"
if content != want {
t.Errorf("Got %q, want %q", content, want)
}
}
Définir / Réinitialiser la fonction de simulation dans les tests
Cet exemple montre comment simuler un appel de fonction sans rapport avec notre test unitaire, puis utiliser l'instruction de defer
pour réaffecter l'appel de fonction simulé à sa fonction d'origine.
var validate = validateDTD
// ParseXML parses b for XML elements and values, and returns them as a map of
// string key/value pairs.
func ParseXML(b []byte) (map[string]string, error) {
// we don't care about validating against DTD in our unit test
if err := validate(b); err != nil {
return err
}
// code to parse b etc.
}
func validateDTD(b []byte) error {
// get the DTD from some external storage, use it to validate b etc.
}
Dans notre test unitaire,
func TestParseXML(t *testing.T) {
// assign the original validate function to a variable.
originalValidate = validate
// use the mockValidate function in this test.
validate = mockValidate
// defer the re-assignment back to the original validate function.
defer func() {
validate = originalValidate
}()
var input []byte
actual, err := ParseXML(input)
// assertion etc.
}
func mockValidate(b []byte) error {
return nil // always return nil since we don't care
}
Test avec la fonction setUp et tearDown
Vous pouvez définir une fonction setUp et tearDown.
- Une fonction setUp prépare votre environnement aux tests.
- Une fonction tearDown effectue une restauration.
C'est une bonne option lorsque vous ne pouvez pas modifier votre base de données et que vous devez créer un objet qui simule un objet apporté par la base de données ou qui doit lancer une configuration dans chaque test.
Un exemple stupide serait:
// Standard numbers map
var numbers map[string]int = map[string]int{"zero": 0, "three": 3}
// TestMain will exec each test, one by one
func TestMain(m *testing.M) {
// exec setUp function
setUp("one", 1)
// exec test and this returns an exit code to pass to os
retCode := m.Run()
// exec tearDown function
tearDown("one")
// If exit code is distinct of zero,
// the test will be failed (red)
os.Exit(retCode)
}
// setUp function, add a number to numbers slice
func setUp(key string, value int) {
numbers[key] = value
}
// tearDown function, delete a number to numbers slice
func tearDown(key string) {
delete(numbers, key)
}
// First test
func TestOnePlusOne(t *testing.T) {
numbers["one"] = numbers["one"] + 1
if numbers["one"] != 2 {
t.Error("1 plus 1 = 2, not %v", value)
}
}
// Second test
func TestOnePlusTwo(t *testing.T) {
numbers["one"] = numbers["one"] + 2
if numbers["one"] != 3 {
t.Error("1 plus 2 = 3, not %v", value)
}
}
Autre exemple: préparer une base de données pour tester et effectuer un retour en arrière
// ID of Person will be saved in database
personID := 12345
// Name of Person will be saved in database
personName := "Toni"
func TestMain(m *testing.M) {
// You create an Person and you save in database
setUp(&Person{
ID: personID,
Name: personName,
Age: 19,
})
retCode := m.Run()
// When you have executed the test, the Person is deleted from database
tearDown(personID)
os.Exit(retCode)
}
func setUp(P *Person) {
// ...
db.add(P)
// ...
}
func tearDown(id int) {
// ...
db.delete(id)
// ...
}
func getPerson(t *testing.T) {
P := Get(personID)
if P.Name != personName {
t.Error("P.Name is %s and it must be Toni", P.Name)
}
}
Afficher la couverture du code au format HTML
Exécuter go test
comme d'habitude, mais avec l'indicateur coverprofile
. Utilisez go tool
pour afficher les résultats au format HTML.
go test -coverprofile=c.out
go tool cover -html=c.out