Sök…


Introduktion

Go har sina egna testanläggningar som har allt som krävs för att köra tester och riktmärken. Till skillnad från i de flesta andra programmeringsspråk finns det ofta inget behov av en separat testram, även om vissa finns.

Grundläggande test

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

För att köra testet använder du bara kommandot go test :

$ go test
ok      test_app    0.005s

Använd -v flaggan för att se resultaten från varje test:

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

Använd sökvägen ./... att testa underkataloger rekursivt:

$ 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

Kör ett särskilt test:
Om det finns flera tester och du vill köra ett specifikt test kan det göras så här:

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

Exempel:

go test -v run=TestSum

Benchmark-test

Om du vill mäta riktmärken lägger du till en testmetod som denna:

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

För att få ett enkelt riktmärke:

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

Tabelldrivna enhetstester

Denna typ av testning är populär teknik för testning med fördefinierade ingångs- och utgångsvärden.

Skapa en fil som heter main.go med innehåll:

package main

import (
    "fmt"
)

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

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

När du kör det med ser du att utgången är 9 . Även om Sum funktionen ser ganska enkel ut, är det en bra idé att testa din kod. För att göra detta skapar vi en annan fil med namnet main_test.go i samma mapp som main.go , som innehåller följande kod:

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

Som ni ser skapas ett segment av anonyma strukturer, var och en med en uppsättning input och det förväntade resultatet. Detta gör att ett stort antal testfall kan skapas tillsammans på ett ställe och sedan köras i en slinga, vilket minskar kodrepetitionen och förbättrar tydligheten.

Exempelstester (självdokumenterande test)

Denna typ av tester ser till att din kod sammanställs korrekt och kommer att visas i den genererade dokumentationen för ditt projekt. Utöver detta kan exempletesterna hävda att ditt test ger rätt resultat.

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
}

För att köra ditt test, kör go test i mappen som innehåller dessa filer ELLER lägg de två filerna i en undermapp med namnet sum och sedan från modermappen köra go test ./sum . I båda fallen får du en utgång som liknar detta:

ok      so/sum    0.005s

Om du undrar hur det här testar din kod är här en annan exempelfunktion som faktiskt misslyckas med testet:

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

    // Output:
    // 5
}

När du kör go test får du följande utgång:

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

Om du vill se dokumentationen för sum paket - bara köra:

go doc -http=:6060

och navigera till http: // localhost: 6060 / pkg / FOLDER / sum / , där FOLDER är den mapp som innehåller sum paketet (i detta exempel so ). Dokumentationen för summanivån ser ut så här:

ange bildbeskrivning här

Testar HTTP-förfrågningar

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

Ställ in / återställ håravfunktion i tester

Det här exemplet visar hur man kan förlora ett funktionssamtal som är irrelevant för vårt enhetstest och sedan använda defer för att tilldela återlämnat funktionssamtal till sin ursprungliga funktion.

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

I vårt enhetstest,

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
}

Testning med funktionen setUp och tearDown

Du kan ställa in en SetUp- och tearDown-funktion.

  • En setUp-funktion förbereder din miljö för tester.
  • En tearDown-funktion gör en rollback.

Detta är ett bra alternativ när du inte kan modifiera din databas och du måste skapa ett objekt som simulerar ett objekt med databas eller behöver initiera en konfiguration i varje test.

Ett dumt exempel skulle vara:

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

Ett annat exempel skulle vara att förbereda databasen för att testa och göra rollback

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

Visa kodtäckning i HTML-format

Kör go test som normalt, men med coverprofile flaggan. Använd sedan go tool att se resultaten som HTML.

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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow