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:
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