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