Suche…


Einführung

Go verfügt über eigene Testeinrichtungen, die alles bieten, um Tests und Benchmarks durchzuführen. Im Gegensatz zu den meisten anderen Programmiersprachen ist ein separates Test-Framework oft nicht erforderlich, obwohl einige vorhanden sind.

Grundtest

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)
    }
}

Um den Test auszuführen, verwenden Sie einfach den Befehl go test :

$ go test
ok      test_app    0.005s

Verwenden Sie das Flag -v , um die Ergebnisse jedes Tests -v :

$ go test -v
=== RUN   TestSum
--- PASS: TestSum (0.00s)
PASS
ok      _/tmp    0.000s

Verwenden Sie den Pfad ./... , um Unterverzeichnisse rekursiv zu testen:

$ 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

Einen bestimmten Test ausführen:
Wenn mehrere Tests vorhanden sind und Sie einen bestimmten Test ausführen möchten, können Sie dies folgendermaßen tun:

go test -v -run=<TestName> // will execute only test with this name

Beispiel:

go test -v run=TestSum

Benchmark-Tests

Wenn Sie Benchmarks messen möchten, fügen Sie eine Testmethode wie folgt hinzu:

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)
    }
}

Dann, um einen einfachen Benchmark auszuführen:

$ go test -bench=. 
BenchmarkSum-8    2000000000             0.49 ns/op
ok      so/sum    1.027s

Tischgesteuerte Unit-Tests

Diese Art des Testens ist eine beliebte Technik zum Testen mit vordefinierten Eingabe- und Ausgabewerten.

Erstellen Sie eine Datei namens main.go mit Inhalt:

package main

import (
    "fmt"
)

func main() {
    fmt.Println(Sum(4, 5))
}

func Sum(a, b int) int {
    return a + b
}

Nach dem Ausführen sehen Sie, dass die Ausgabe 9 . Obwohl die Sum Funktion recht einfach aussieht, sollten Sie Ihren Code testen. Dazu erstellen wir eine weitere Datei namens main_test.go im selben Ordner wie main.go mit folgendem Code:

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)
        }
    }
}

Wie Sie sehen, wird ein Teil der anonymen Strukturen erstellt, die jeweils eine Reihe von Eingaben und das erwartete Ergebnis enthalten. Dies ermöglicht die Erstellung einer großen Anzahl von Testfällen an einem Ort, die dann in einer Schleife ausgeführt werden, wodurch die Codewiederholung reduziert und die Übersichtlichkeit verbessert wird.

Beispieltests (Selbstdokumentationstests)

Diese Art von Tests stellt sicher, dass Ihr Code ordnungsgemäß kompiliert wird und in der generierten Dokumentation für Ihr Projekt angezeigt wird. Darüber hinaus können die Beispieltests behaupten, dass Ihr Test eine korrekte Ausgabe erzeugt.

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
}

Um Ihren Test ausführen ausführen go test - sum go test ./sum go test in den Ordner, die Dateien oder die zwei Dateien in einem Unterordner namens setzen sum und dann aus den übergeordneten Ordner ausführen go test ./sum . In beiden Fällen erhalten Sie eine Ausgabe ähnlich der folgenden:

ok      so/sum    0.005s

Wenn Sie sich fragen, wie dies Ihren Code testet, finden Sie hier eine weitere Beispielfunktion, die den Test tatsächlich nicht besteht:

func ExampleSum_fail() {
    x := Sum(1, 2)
    fmt.Println(x)

    // Output:
    // 5
}

Wenn Sie go test ausführen, erhalten Sie folgende Ausgabe:

$ go test
--- FAIL: ExampleSum_fail (0.00s)
got:
3
want:
5
FAIL
exit status 1
FAIL    so/sum    0.006s

Wenn Sie die Dokumentation für Ihr sum anzeigen möchten, führen Sie einfach Folgendes aus:

go doc -http=:6060

und navigieren Sie zu http: // localhost: 6060 / pkg / FOLDER / sum / , wobei FOLDER der Ordner ist, der das sum (in diesem Beispiel so ). Die Dokumentation für die Summenmethode sieht folgendermaßen aus:

Geben Sie hier die Bildbeschreibung ein

HTTP-Anforderungen testen

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)
    }
}

Mock-Funktion in Tests setzen / zurücksetzen

In diesem Beispiel wird gezeigt, wie Sie einen Funktionsaufruf, der für unseren defer relevant ist, defer und dann mit der defer Anweisung den übergebenen Funktionsaufruf wieder seiner ursprünglichen Funktion zuweisen.

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.
}

In unserem Unit-Test

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
}

Testen mit der Funktion setUp und tearDown

Sie können eine SetUp- und TearDown-Funktion einstellen.

  • Eine SetUp-Funktion bereitet Ihre Umgebung für Tests vor.
  • Eine TearDown-Funktion führt ein Rollback durch.

Dies ist eine gute Option, wenn Sie Ihre Datenbank nicht ändern können und ein Objekt erstellen müssen, das ein von der Datenbank mitgebrachtes Objekt simuliert oder bei jedem Test eine Konfiguration initiiert.

Ein dummes Beispiel wäre:

// 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)
    }
}

Ein anderes Beispiel wäre das Vorbereiten der Datenbank zum Testen und Ausführen eines Rollbacks

 // 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)
    }
}

Anzeigen der Codeabdeckung im HTML-Format

Führen Sie den go test wie üblich aus, jedoch mit dem coverprofile Flag. Verwenden Sie go tool um die Ergebnisse als HTML anzuzeigen.

    go test -coverprofile=c.out
    go tool cover -html=c.out


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow