Przejdź do treści

Golang Warsaw #2

Dziś odbyło się drugie spotkanie Golang Warsaw. Jest to nowa organizacja w naszym mieście (dopiero drugie spotkanie w systemie comiesięcznym). Wiodącym językiem Go.

Będzie to moje pierwsze spotkanie z ludźmi pracującym w tym języku. Nie ukrywam, że chciałbym dowiedzieć się czegoś o tym nowym, tak mocno reklamowanym języku.

Godzina 18:15 - Zaczynamy!

Baner promujący artykuł

Talk #1: Testowanie w Go

Pierwsza prelekcja - Tomasz Grodzki (github.com/tg) opowiedział o testowaniu aplikacji napisanym w Go. Miałem nadzieje, że dowiem się czegoś o testowaniu oraz debugowaniu aplikacji.

Tomek powiedział, że w Go można łatwo pisać aplikację na wielu procesorach, ze względu na goroutines, które runtime Go sam rozkłada na pulę wątków systemowych. Go ma wiele wbudowanych rzeczy, m. in. biblioteka pomocna w testach (testing).

Testowanie kodu napisanego w Go

Pisanie pierwszego testu: testy umieszczamy w osobnym pliku, którego nazwa kończy się na _test.go, np. party_test.go.

Pierwsza metoda:

package party

import "testing"

func TestBeer(t * testing.T) {
    if !FridgeContains("beer") {
        t.Fatal("expected some cold beer");
    }
}

Druga metoda (from outside):

package party_test

import (
    "testing"
    "rihanna.com/party"
)

func TestBeer(t *testing.T) {
    if !party.FridgeContains("beer") {
        t.Fatal("expected some cold beer");
    }
}

Uruchomienie testów

go test .
go test rihanna.com/party
go test ./... # odpala wszystkie testy w głąb

testing.T - nasze magiczne ‘t’. Biblioteka pomocna w testach. Zawiera mnóstwo metod tj: Error, Errorf, Fail, FailNow, Failed, Fatal, Fatalf, Log, Logf, Parallel, Skip, SkipNow, Skipf, Skipped.

Logi pokazują się tylko w przypadku kiedy dany test nie przeszedł. Jeśli uruchomimy testy w parametrem verbose, pokażemy wszystkie logi (nawet w tych testach które przeszły).

Testy z parallel będą się uruchamiały jednocześnie.

Examples - część testu

func ExampleHandsUp() {
    fmt.Println(HandsUp("o o o"))
    // Output:
    // \o/ \o/ \o/
}

func Example() // package
func ExampleF() // function
func ExampleT() // type
func ExampleT_M() // method

func Example*_xyz() // more...

Dokumentacja

godoc -ex=true . # budowanie dokumentacji

Narzędzie godoc potrafiło postawić webserwer (godoc -http=:6060) oraz wygenerować dokumentację HTML. Dziś nie ma go już w dystrybucji Go: w terminalu używa się go doc, a dokumentację HTML daje pkgsite.

Trzeci rodzaj testów to benchmarks.

func BenchmarkHeadSpin(b *testing.B) {
    for i := 0; i < b.N; i++ {
        HeadSpin()
    }
}

Uruchamiamy je za pomocą:

go test -bench . # ta kropka to wyrażenie regularne

go test sam sobie dopiera ile iteracji ma zrobić w ciągu zadanego okresu czasowego. Parametr benchtime zmienia ten okres czasu.

testing.B = testing.T + (ReportAllocs, ResetTimer, RunParallel, SetBytes, SetParallelism, StartTimer, StopTimer)

  • ReportAllocs - raportuje alokację pamięci

Więcej kontroli

// since go 1.4
func TestMain(m *testing.M) {
    os.Exit(m.Run())
}

Tyle odnośnie podstaw testowania

Jeśli do go test przekażesz flagę, której go test nie rozpozna przekaże te parametry do binarki testowej (można je odczytać w TestMain).

W bibliotece standardowej mamy kilka rzeczy, które ułatwiają testowanie:

  • net/httptest
  • testing/iotest - do testowania wejścia / wyjścia
  • testing/quick - do testowania: generuje losowe argumenty naszej funkcji

net / httptest

  • httptest.ResponseRecorder
  • httptest.Server - automatycznie nasłuchuje na localhost z wybranym przez system porcie, oraz zwraca nam URL do siebie

testing / iotest

  • iotest.DataErrReader
  • iotest.TimeoutReader - za drugim razem gdy będzie czytał, zwróci iotest.ErrTimeout
  • iotest.HalfReader - czyta dane do połowy - pomocne do testowania, co się stanie, jeśli nasza fn dostanie mniej danych niż się spodziewa
  • iotest.OneByteReader - czyta po jednym bajcie na wywołanie, czyli sprawdza, czy nasza fn radzi sobie z danymi przychodzącymi w małych kawałkach
  • iotest.TruncateWriter - zapisze przekazaną liczbę bajtów, ale poinformuje, że zapisał komplet danych - taka oszukańcza fn 😄

testing / quick

  • Black box testing
  • Generuje losowe ciągi znaków
  • quick.Check and quick.CheckEqual
  • inspirowana QuickCheck z Haskell-a
  • dziś pakiet jest zamrożony, a od Go 1.18 w testing jest wbudowany fuzzing (func FuzzXxx(f *testing.F))

Do budowania losowych argumentów testing/quick używa pakietu reflect, który służy do refleksji, czyli badania typów i wartości w czasie działania programu - więcej pkg.go.dev/reflect

quick.Config

type Config struct {
    // Set max number of iterations
    MaxCount int

    // Scale max number of iterations
    MaxCountScale float64

    // Source of random number
    Rand *rand.Rand

    // Generator of values
    Values func ([]reflect.Value, *rand.Rand)
}

Polecane narzędzia

  • go vet - sprawdza popularne błędy w kodzie
  • -race flag - stara się wykryć kiedy używamy zmienny z różnych go routings
  • Go bardzo lubi interfejsy, powinniśmy korzystać z nich, ponieważ lepiej się potem testuje

Profile

go test pozwala zapisywać wynik do pliku.

  • go test -coverprofile cover.out
  • go test -cpuprofile cpu.out
  • go test -memprofile mem.out

W 2015 roku -cpuprofile dawał na OS X niewiarygodne wyniki: github.com/golang/go/issues/2008. Problem zniknął w OS X 10.11.

Pokrycie kodu

go test -cover

# prezentacja na stronie internetowej
go test -coverprofile=c.pro
go tool cover -html=c.pro

Referencje

  • golang.org/pkg/testing
  • blog.golang.org
  • go help test
  • go help testflag

•

Talk #2

Była tylko jedna prelekcja, niestety, ze względu na chorobę drugiego prelegenta.

Do zobaczenia na następnym Golang!

Cya! 👋