Zoeken…


Invoering

Go heeft zijn eigen testfaciliteiten die alles hebben wat nodig is om tests en benchmarks uit te voeren. Anders dan in de meeste andere programmeertalen, is er vaak geen behoefte aan een afzonderlijk testraamwerk, hoewel er enkele bestaan.

Basistest

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

Gebruik de opdracht go test voeren:

$ go test
ok      test_app    0.005s

Gebruik de vlag -v om de resultaten van elke test te bekijken:

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

Gebruik het pad ./... om submappen recursief te 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

Voer een bijzondere test uit:
Als er meerdere tests zijn en u wilt een specifieke test uitvoeren, kan dit als volgt worden gedaan:

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

Voorbeeld:

go test -v run=TestSum

Benchmarktests

Als u benchmarks wilt meten, voeg dan een testmethode als volgt toe:

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

Vervolgens om een eenvoudige benchmark uit te voeren:

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

Tabel-aangedreven eenheidstests

Dit type testen is een populaire techniek voor testen met vooraf gedefinieerde invoer- en uitvoerwaarden.

Maak een bestand met de naam main.go met inhoud:

package main

import (
    "fmt"
)

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

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

Nadat u het hebt uitgevoerd, ziet u dat de uitvoer 9 . Hoewel de Sum functie vrij eenvoudig lijkt, is het een goed idee om uw code te testen. Om dit te doen, maken we een ander bestand met de naam main_test.go in dezelfde map als main.go , met de volgende 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)
        }
    }
}

Zoals u kunt zien, wordt een schijfje anonieme structs gemaakt, elk met een set invoer en het verwachte resultaat. Hierdoor kan een groot aantal testgevallen allemaal samen op één plaats worden gemaakt en vervolgens in een lus worden uitgevoerd, waardoor herhaling van de code wordt verminderd en de duidelijkheid wordt verbeterd.

Voorbeeldtests (zelfdocumentatietests)

Dit type tests zorgt ervoor dat uw code correct wordt gecompileerd en wordt weergegeven in de gegenereerde documentatie voor uw project. Daarnaast kunnen de voorbeeldtests beweren dat uw test de juiste uitvoer oplevert.

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
}

Om uw test uit te voeren, voert u go test in de map met die bestanden OF plaatst u deze twee bestanden in een submap met de naam sum en voert u vervolgens vanuit de bovenliggende map go test ./sum . In beide gevallen krijgt u een uitvoer vergelijkbaar met deze:

ok      so/sum    0.005s

Als je je afvraagt hoe dit je code test, is hier een andere voorbeeldfunctie, die eigenlijk de test niet doorstaat:

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

    // Output:
    // 5
}

Wanneer u de go test , krijgt u de volgende uitvoer:

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

Als u de documentatie voor uw sum wilt bekijken, voert u gewoon uit:

go doc -http=:6060

en navigeer naar http: // localhost: 6060 / pkg / FOLDER / sum / , waarbij FOLDER de map is die het sum bevat (in dit voorbeeld so ). De documentatie voor de sommethode ziet er als volgt uit:

voer hier de afbeeldingsbeschrijving in

HTTP-aanvragen 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-functie instellen / resetten in tests

In dit voorbeeld wordt getoond hoe een functieaanroep die niet relevant is voor onze eenheidstest, wordt bespot en vervolgens de defer om de bespotte functieaanroep opnieuw toe te wijzen aan de oorspronkelijke functie.

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 onze 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 met de functie setUp en tearDown

U kunt een setUp and tearDown-functie instellen.

  • Een set-upfunctie bereidt uw omgeving voor op tests.
  • Een tearDown-functie doet een terugdraaiing.

Dit is een goede optie wanneer u uw database niet kunt wijzigen en u een object moet maken dat een object uit de database simuleert of bij elke test een configuratie moet initiëren.

Een stom voorbeeld zou zijn:

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

Een ander voorbeeld zou zijn om de database voor te bereiden om te testen en om terug te draaien

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

Bekijk codedekking in HTML-formaat

Voer de go test zoals normaal, maar met de coverprofile vlag. Gebruik vervolgens de go tool om de resultaten als HTML te bekijken.

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


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow