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:

entrer la description de l'image ici

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


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow