Hlavní navigace

Testování Go aplikací s využitím knihovny GΩmega a frameworku Ginkgo

Pavel Tišnovský

Důležitému tématu testování aplikací s využitím jazyka Go se budeme zabývat i dnes, protože si popíšeme velmi zajímavou knihovnu nazvanou GΩmega, kterou lze velmi snadno zkombinovat s frameworkem Ginkgo určeným pro tvorbu BDD testů.

Doba čtení: 39 minut

Sdílet

11. Framework Ginkgo

12. Příprava projektu pro otestování s využitím frameworku Ginkgo

13. Vygenerování kostry sady BDD testů

14. Vytvoření kostry BDD testu

15. Implementace BDD testu

16. Rozdělení složitějšího BDD testu do bloků

17. Podrobnější výpis průběhu testů

18. Chování frameworku Ginkgo při chybě

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Testování Go aplikací s využitím knihovny GΩmega a frameworku Ginkgo

Již několik částí tohoto seriálu bylo věnováno problematice testování, ať již přímo psaní testů pro aplikace vyvinuté v jazyku Go (jednotkové testy, BDD), nebo použití Go pro testování REST API, testování aplikací s textovým uživatelským rozhraním atd. Tomuto důležitému tématu se budeme věnovat i dnes, protože si popíšeme velmi zajímavou knihovnu nazvanou GΩmega, kterou lze velmi snadno zkombinovat s frameworkem Ginkgo určeným pro tvorbu BDD testů.

Obrázek 1: Logo projektu GΩmega

2. Knihovna Gomega

Knihovna nazvaná Gomega nabízí programátorům velké množství takzvaných „matcherů“, což jsou – poněkud zjednodušeně řečeno – funkce kontrolující, jestli byla splněna nějaká podmínka. V případě, že podmínka splněna není, nahlásí se chyba, ovšem současně se vypíšou i přesné okolnosti, za jakých došlo k nesplnění podmínky. Jedná se o velmi užitečný mechanismus s vlastnostmi, kterých je v samotném jazyku Go (bez použití dalších knihoven) poměrně těžké dosáhnout, protože nejsou podporovány aserce a všechny podmínky je tak nutné zapisovat explicitně s využitím konstrukce if (což je ovšem informace, která se neobjeví ve výsledcích testů).

Poznámka: ve skutečnosti není knihovna Gomega jedinou podobně koncipovanou knihovnou určenou pro Go. V předchozích článcích jsme se již mohli seznámit s knihovnami GoConvey a Oglematchers s přibližně podobnou funkcionalitou.

Podívejme se nyní, jaké „matchery“ v knihovně Omega nalezneme. U každého matcheru je napsána oblast, které se týká i stručný popis jeho činnosti:

Jméno Oblast Stručný popis
Equal obecné hodnoty základní kontrola – ekvivalence
BeEquivalentTo obecné hodnoty test na ekvivalenci k zadané hodnotě
BeIdenticalTo obecné hodnoty test na identitu
BeAssignableToTypeOf obecné hodnoty test, zda je možné provést přiřazení
     
BeNil nulové hodnoty test na hodnotu nil
BeZero nulové hodnoty test na nulu
     
BeTrue pravdivostní hodnoty test na hodnotu true
BeFalse pravdivostní hodnoty test na hodnotu false
     
HaveOccurred chyby test zda došlo k chybě
Succeed chyby test zda nedošlo k chybě
MatchError chyby test zda došlo k určité chybě
Panic chyby test, zda se zavolala funkce panic
     
BeClosed kanály kanál je uzavřen
Receive kanály kanál přijal zadaný prvek
BeSent kanály do kanálu byl odeslán zadaný prvek
     
BeAnExistingFile soubory kontrola existence souboru
BeARegularFile soubory kontrola existence běžného souboru (ne adresáře)
BeADirectory soubory kontrola existence adresáře
     
ContainSubstring řetězce test na existenci podřetězce
HavePrefix řetězce test na existenci prefixu řetězce
HaveSuffix řetězce test na existenci postfixu řetězce
MatchRegexp řetězce test, jestli řetězec odpovídá regulárnímu výrazu
MatchJSON řetězce dtto, ale pro řetězec obsahující reprezentaci JSONu
MatchXML řetězce dtto, ale pro řetězec obsahující reprezentaci XML
MatchYAML řetězce dtto, ale pro řetězec obsahující reprezentaci YAMLu
     
HaveLen kolekce kolekce má zadanou velikost
HaveCap kolekce kolekce má zadanou kapacitu
ContainElement kolekce kolekce obsahuje element
BeElementOf kolekce element je prvkem kolekce
ConsistOf kolekce kolekce obsahuje
HaveKey kolekce mapa má dvojici se zadaným klíčem
HaveKeyWithValue kolekce mapa má dvojici se zadaným klíčem a hodnotou
     
BeNumerically číselné hodnoty test na hodnotu bez ohledu na typ
BeTemporally číselné hodnoty dtto, ale pro časové razítko
     
And spojky splněny musí být všechny podmínky
SatisfyAll spojky splněny musí být všechny podmínky
Or spojky splněna musí být alespoň jedna podmínka
SatisfyAny spojky splněna musí být alespoň jedna podmínka
     
Not spojky logická negace podmínky

3. Testovaná funkce pro výpočet faktoriálu

Několik demonstračních příkladů, které si dnes ukážeme, bude z důvodu stručnosti postaveno na testování klasické „školní“ funkce určené pro výpočet faktoriálu. Jeden z možných rekurzivních zápisů výpočtu faktoriálu může vypadat následovně:

package main
 
func Factorial(n int64) int64 {
        switch {
        case n < 0:
                return 1
        case n == 0:
                return 1
        default:
                return n * Factorial(n-1)
        }
}
Poznámka: první podmínka a vůbec použití datového typu int64 namísto uint64 sice může vypadat samoúčelně, ale má svůj význam, protože funkci později rozšíříme takovým způsobem, aby pro neplatný vstup vracela chybu (i když existuje poměrně logické rozšíření výpočtu faktoriálu i na záporná čísla s využitím gama funkce).

V případě, že se jednotkové testy kontrolující činnost funkce Factorial vytváří pouze s využitím standardní knihovny testing, bude kód testů poměrně dlouhý, protože je nutné explicitně zapsat a otestovat všechny podmínky jen pomocí základních konstrukcí programovacího jazyka Go. Pokud nebudou testy založeny na tabulce, může jejich zápis vypadat následovně:

package main
 
import (
        "testing"
)
 
func TestFactorialForZero(t *testing.T) {
        result := Factorial(0)
        if result != 1 {
                t.Errorf("Expected that 0! == 1, but got %d instead", result)
        }
}
 
func TestFactorialForOne(t *testing.T) {
        result := Factorial(1)
        if result != 1 {
                t.Errorf("Expected that 1! == 1, but got %d instead", result)
        }
}
 
func TestFactorialForSmallNumber(t *testing.T) {
        result := Factorial(5)
        if result <= 10 || result >= 10000 {
                t.Errorf("Expected that 5! == is between 10..10000")
        }
}
 
func TestFactorialForSmallNumberNegative(t *testing.T) {
        result := Factorial(20)
        if result <= 10 || result >= 10000 {
                t.Errorf("Expected that 20! == is between 10..10000")
        }
}
 
func TestFactorialForTen(t *testing.T) {
        result := Factorial(10)
        expected := int64(3628800)
        if result != expected {
                t.Errorf("Expected that 10! == %d, but got %d instead", expected, result)
        }
}
 
func TestFactorialForBigNumber(t *testing.T) {
        result := Factorial(20)
        if result <= 0 {
                t.Errorf("Expected that 20! > 0, but got negative number %d instead", result)
        }
}
 
func TestFactorialForEvenBiggerNumber(t *testing.T) {
        result := Factorial(30)
        if result <= 0 {
                t.Errorf("Expected that 30! > 0, but got negative number %d instead", result)
        }
}

Výsledky takto naprogramovaných jednotkových testů získáme po spuštění příkazu:

$ go test -v

Zobrazit by se měly následující zprávy informující uživatele o průběhu celého testu:

=== RUN   TestFactorialForZero
--- PASS: TestFactorialForZero (0.00s)
=== RUN   TestFactorialForOne
--- PASS: TestFactorialForOne (0.00s)
=== RUN   TestFactorialForSmallNumber
--- PASS: TestFactorialForSmallNumber (0.00s)
=== RUN   TestFactorialForSmallNumberNegative
--- FAIL: TestFactorialForSmallNumberNegative (0.00s)
    factorial_test.go:31: Expected that 20! == is between 10..10000
=== RUN   TestFactorialForTen
--- PASS: TestFactorialForTen (0.00s)
=== RUN   TestFactorialForBigNumber
--- PASS: TestFactorialForBigNumber (0.00s)
=== RUN   TestFactorialForEvenBiggerNumber
--- FAIL: TestFactorialForEvenBiggerNumber (0.00s)
    factorial_test.go:53: Expected that 30! > 0, but got negative number -8764578968847253504 instead
FAIL
exit status 1
FAIL    _/home/tester/go-root/article_45/factorial1        0.005s
Poznámka: samozřejmě nám nic nebrání použít „table-driven“ přístup, který je v komunitě Go vývojářů velmi oblíbený, zejména při tvorbě jednotkových testů pro funkce bez vedlejších efektů. V našem konkrétním případě by bylo možné použít například takto upravený „table-driven“ test:
package main
 
import (
        "testing"
)
 
type factorialEntry struct {
        n        int64
        expected int64
}
 
func TestFactorial(t *testing.T) {
        var entries = []factorialEntry{
                {0, 1},
                {1, 1},
                {2, 2},
                {3, 6},
                {9, 362880},
                {10, 3628800},
                {20, 2432902008176640000},
                {-1, 1},
        }
        for _, entry := range entries {
                computed := Factorial(entry.n)
                if computed != entry.expected {
                        t.Errorf("%d! != %d, but %d", entry.n, computed, entry.expected)
                } else {
                        t.Logf("factorial computer correctly for input %d", entry.n)
                }
        }
}

S výsledky:

=== RUN   TestFactorial
--- PASS: TestFactorial (0.00s)
    factorial_2_test.go:28: factorial computer correctly for input 0
    factorial_2_test.go:28: factorial computer correctly for input 1
    factorial_2_test.go:28: factorial computer correctly for input 2
    factorial_2_test.go:28: factorial computer correctly for input 3
    factorial_2_test.go:28: factorial computer correctly for input 9
    factorial_2_test.go:28: factorial computer correctly for input 10
    factorial_2_test.go:28: factorial computer correctly for input 20
    factorial_2_test.go:28: factorial computer correctly for input -1
PASS
ok      _/home/tester/temp/go-root/article_45/factorial1        0.004s

4. Kostra jednotkových testů vytvořených s využitím knihovny Gomega

Nyní si ukažme, jak by mohla vypadat kostra jednotkových testů v případě, že kromě standardní knihovny Testing použijeme i knihovnu Gomega. Nejdříve je nutné vytvořit strukturu představující instanci Gomega a předat jí ukazatel na strukturu typu testing.T:

g := NewGomegaWithT(t)

Následně již můžeme volat metody implementované touto strukturou a – což je v praxi mnohem důležitější a užitečnější – tyto metody zřetězit do podoby představující testovanou podmínku. Pokud tedy potřebujeme otestovat, zda se faktoriál vstupní hodnoty 0 rovná jedničce, musíme napsat:

g.Expect(Factorial(0)).To(Equal(int64(1)))
Poznámka: přetypování je zde nutné, protože v opačném případě by se porovnávaly jak hodnoty, tak i jejich typy. A v programovacím jazyce Go platí (velmi rozumně!), že se neprovádí automatické přetypování, a to ani za předpokladu, že int bude aliasem typu int64 (v závislosti na platformě).

První tři testy na vstupní hodnoty 0, 1 a 10 lze zapsat takto:

package main
 
import (
        . "github.com/onsi/gomega"
        "testing"
)
 
func TestFactorialForZero(t *testing.T) {
        g := NewGomegaWithT(t)
        g.Expect(Factorial(0)).To(Equal(int64(1)))
}
 
func TestFactorialForOne(t *testing.T) {
        g := NewGomegaWithT(t)
        g.Expect(Factorial(1)).To(Equal(int64(1)))
}
 
func TestFactorialForTen(t *testing.T) {
        g := NewGomegaWithT(t)
        g.Expect(Factorial(10)).To(Equal(int64(3628800)))
}

Výsledek běhu takto zapsaných testů získáme opět příkazem:

$ go test -v

Zde konkrétně:

=== RUN   TestFactorialForZero
--- PASS: TestFactorialForZero (0.00s)
=== RUN   TestFactorialForOne
--- PASS: TestFactorialForOne (0.00s)
=== RUN   TestFactorialForTen
--- PASS: TestFactorialForTen (0.00s)
PASS
ok      factorial.go    0.005s

5. Alternativní způsob zápisu jednotkových testů

Alternativně je možné použít zápis, v němž se vyskytuje Unicode znak Ω (jméno funkce) a nepoužívá se objekt získaný voláním NewGomegaWithT:

package main
 
import (
        . "github.com/onsi/gomega"
        "testing"
)
 
func TestFactorialForZero(t *testing.T) {
        RegisterTestingT(t)
        Ω(Factorial(0)).To(Equal(int64(1)))
}
 
func TestFactorialForOne(t *testing.T) {
        RegisterTestingT(t)
        Ω(Factorial(1)).To(Equal(int64(1)))
}
 
func TestFactorialForTen(t *testing.T) {
        RegisterTestingT(t)
        Ω(Factorial(10)).To(Equal(int64(3628800)))
}
Poznámka: připomeňme si, že ve zdrojových kódech programovacího jazyka Go je možné použít celý rozsah znaků Unicode a pro ukládání zdrojových souborů je vždy použito UTF-8, nezávisle na tom, jak je nakonfigurovaný operační systém (to je velký krok kupředu oproti mnoha dalším mainstreamovým jazykům).

Výsledek běhu takto zapsaných testů by měl být shodný s předchozím příkladem:

=== RUN   TestFactorialForZero
--- PASS: TestFactorialForZero (0.00s)
=== RUN   TestFactorialForOne
--- PASS: TestFactorialForOne (0.00s)
=== RUN   TestFactorialForTen
--- PASS: TestFactorialForTen (0.00s)
PASS
ok      factorial.go    0.006s

6. Porovnání numerických hodnot a použití klauzule SatisfyAll

Testy uvedené v předchozích dvou kapitolách sice byly zapsány korektně, ale měly několik nevýhod. Především se muselo provádět explicitní přetypování int na int64 a taktéž nebylo možné zapsat podmínky typu „je menší“, „je větší“ atd. Oba nedostatky lze vyřešit s využitím metody BeNumerically, které se předá jak testovaná relace (relační operátor zapsaný ve formě řetězce), tak i číselná hodnota. Případné konverze jsou provedeny zcela automaticky takovým způsobem, aby se skutečně porovnávaly hodnoty a nikoli i jejich typy:

g.Expect(Factorial(0)).To(BeNumerically("==", 1))
g.Expect(Factorial(5)).To(BeNumerically("<=", 10000))

Navíc je možné specifikovat, že některé podmínky musí být splněny současně. K tomuto účelu se používá klauzule SatisfyAll, kterou lze zkrátit na And, ovšem první jméno je v oblasti testů příhodnější. Můžeme tedy relativně snadno napsat test kontrolující, jestli se výsledek výpočtu faktoriálu nachází v nastavených mezích:

g.Expect(Factorial(5)).To(SatisfyAll(BeNumerically(">=", 10), BeNumerically("<=", 10000)))
Poznámka: pochopitelně se nemusíme omezovat pouze na dvě podmínky, které mají být splněny současně.

Podívejme se nyní na upravené a rozšířené jednotkové testy kontrolující činnost funkce pro výpočet faktoriálu:

package main
 
import (
        . "github.com/onsi/gomega"
        "testing"
)
 
func TestFactorialForZero(t *testing.T) {
        g := NewGomegaWithT(t)
        g.Expect(Factorial(0)).To(BeNumerically("==", 1))
}
 
func TestFactorialForOne(t *testing.T) {
        g := NewGomegaWithT(t)
        g.Expect(Factorial(1)).To(BeNumerically("==", 1))
}
 
func TestFactorialForTen(t *testing.T) {
        g := NewGomegaWithT(t)
        g.Expect(Factorial(10)).To(BeNumerically("==", 3628800))
}
 
func TestFactorialForSmallNumber(t *testing.T) {
        g := NewGomegaWithT(t)
        g.Expect(Factorial(5)).To(SatisfyAll(BeNumerically(">=", 10), BeNumerically("<=", 10000)))
}

Výsledky, které získáme po spuštění těchto testů:

=== RUN   TestFactorialForZero
--- PASS: TestFactorialForZero (0.00s)
=== RUN   TestFactorialForOne
--- PASS: TestFactorialForOne (0.00s)
=== RUN   TestFactorialForTen
--- PASS: TestFactorialForTen (0.00s)
=== RUN   TestFactorialForSmallNumber
--- PASS: TestFactorialForSmallNumber (0.00s)
PASS
ok      factorial.go    0.006s

7. Funkce pro výpočet faktoriálu vracející chybu

Funkce pro výpočet faktoriálu, kterou jsme si ukázali ve třetí kapitole byla vytvořena ve stylu, který je očekáván u aplikací naprogramovaných například v jazyku C. Chybový stav je v případě potřeby signalizován speciální hodnotou. Pokud bychom například potřebovali signalizovat, že výpočet faktoriálu záporného čísla nelze vypočítat, mohla by se provést tato úprava:

func Factorial(n int64) int64 {
        switch {
        case n < 0:
                return -1 // speciální chybová hodnota
        case n == 0:
                return 1
        default:
                return n * Factorial(n-1)
        }
}

V tomto případě se ovšem nejedná o idiomatický kód programovacího jazyka Go. V Go je totiž dobrým zvykem, aby byla chyba signalizována skutečným objektem reprezentujícím chybu. Pokud funkce vrací hodnotu signalizující chybu, bývá tato hodnota vždy vrácena jako poslední. Výpočet faktoriálu tedy můžeme přepsat následovně (stále se jedná o školní příklad neaspirující na nejvyšší rychlost a nejnižší spotřebu operační paměti):

package main
 
import "errors"
 
func Factorial(n int64) (int64, error) {
        switch {
        case n < 0:
                return 0, errors.New("math: factorial of negative number?!?")
        case n == 0:
                return 1, nil
        default:
                ret, err := Factorial(n - 1)
                if err != nil {
                        return 0, err
                }
                return n * ret, nil
        }
}

8. Rozšíření jednotkových testů o podmínku, zda došlo či naopak nedošlo k chybě

Pro zjištění, zda testovaná funkce vrátila či nevrátila chybu, slouží klauzule HaveOccurred a Succed. Tyto klauzule se používají pouze společně s případnou chybovou hodnotou popř. hodnotou nil. Pokud pouze potřebujeme zjistit, že došlo k očekávané chybě (vrácení struktury reprezentující chybu), můžeme napsat:

_, err := Factorial(-1)
g.Expect(err).Should(HaveOccurred())

Zápis opačné podmínky používá negovanou podmínku ShouldNot:

_, err := Factorial(0)
g.Expect(err).ShouldNot(HaveOccurred())

V tomto případě ovšem bývá lepší zapsat podmínku bez negace:

_, err := Factorial(0)
g.Expect(err).Should(Succeed())

Zajímavé je, že například výše popsaná podmínka BeNumerically je použitelná i ve chvíli, kdy porovnáváme dvě návratové hodnoty funkce – vlastní hodnotu faktoriálu a případnou informaci o chybě:

g.Expect(Factorial(10)).To(BeNumerically("==", 3628800))
g.Expect(Factorial(5)).To(SatisfyAll(BeNumerically(">=", 10), BeNumerically("<=", 10000)))

V tomto případě pochopitelně platí, že pokud funkce signalizuje chybu, bude to testem detekováno, a to i tehdy, pokud by první vrácená hodnota byla (čistě náhodou) korektní.

Podívejme se nyní na to, jakým způsobem lze rozšířit předchozí variantu jednotkových testů o další podmínky:

package main
 
import (
        . "github.com/onsi/gomega"
        "testing"
)
 
func TestFactorialForNegativeValue(t *testing.T) {
        g := NewGomegaWithT(t)
        _, err := Factorial(-1)
        g.Expect(err).Should(HaveOccurred())
}
 
func TestFactorialForZero(t *testing.T) {
        g := NewGomegaWithT(t)
        result, err := Factorial(0)
        g.Expect(err).ShouldNot(HaveOccurred())
        g.Expect(result).To(BeNumerically("==", 1))
}
 
func TestFactorialForOne(t *testing.T) {
        g := NewGomegaWithT(t)
        result, err := Factorial(0)
        g.Expect(err).Should(Succeed())
        g.Expect(result).To(BeNumerically("==", 1))
}
 
func TestFactorialForTen(t *testing.T) {
        g := NewGomegaWithT(t)
        g.Expect(Factorial(10)).To(BeNumerically("==", 3628800))
}
 
func TestFactorialForSmallNumber(t *testing.T) {
        g := NewGomegaWithT(t)
        g.Expect(Factorial(5)).To(SatisfyAll(BeNumerically(">=", 10), BeNumerically("<=", 10000)))
}

Očekávané výsledky testů:

=== RUN   TestFactorialForNegativeValue
--- PASS: TestFactorialForNegativeValue (0.00s)
=== RUN   TestFactorialForZero
--- PASS: TestFactorialForZero (0.00s)
=== RUN   TestFactorialForOne
--- PASS: TestFactorialForOne (0.00s)
=== RUN   TestFactorialForTen
--- PASS: TestFactorialForTen (0.00s)
=== RUN   TestFactorialForSmallNumber
--- PASS: TestFactorialForSmallNumber (0.00s)
PASS
ok      factorial.go    0.006s

9. Zprávy vypsané ve chvíli, kdy je výpočet nekorektní

Nyní si ukažme, jak se knihovna Gomega chová ve chvíli, kdy je výpočet faktoriálu naprogramován nekorektně, tedy tak, aby vracel chybné hodnoty. „Rozbití“ výpočtu lze samozřejmě provést mnoha různými způsoby, například špatně vypočtenou hodnotou pro rekurzivní výpočet:

package main
 
import "errors"
 
func Factorial(n int64) (int64, error) {
        switch {
        case n < 0:
                return 0, errors.New("math: factorial of negative number?!?")
        case n == 0:
                return 1, nil
        default:
                ret, err := Factorial(n - 2)
                if err != nil {
                        return 0, err
                }
                return n * ret, nil
        }
}

Spuštění testů podle očekávání povede k detekci chyby, ovšem zajímavější je sledovat, jaké informace se vypíšou. To je totiž velmi důležité – setkal jsem se již s mnoha projekty (vytvořenými v jiném programovacím jazyce), které při testování jen lakonicky vypíšou „assertion failed“ s již nedopsaným dodatkem „hledej Šmudlo“. Knihovna Gomega je ve chvíli, kdy dojde k chybě, velmi upovídaná, což je jen dobře (pokud naopak test projde bez chyby, nevypisuje se žádná dodatečná informace a to je taky správné chování):

=== RUN   TestFactorialForNegativeValue
--- PASS: TestFactorialForNegativeValue (0.00s)
=== RUN   TestFactorialForZero
--- PASS: TestFactorialForZero (0.00s)
=== RUN   TestFactorialForOne
--- PASS: TestFactorialForOne (0.00s)
=== RUN   TestFactorialForTen
--- FAIL: TestFactorialForTen (0.00s)
    factorial_test.go:30:
        Expected
            <int64>: 3840
        to be ==
            <int>: 3628800
=== RUN   TestFactorialForSmallNumber
--- FAIL: TestFactorialForSmallNumber (0.00s)
    factorial_test.go:35:
        Unexpected non-nil/non-zero extra argument at index 1:
                <*errors.errorString>: &errors.errorString{s:"math: factorial of negative number?!?"}
FAIL
exit status 1
FAIL    factorial.go    0.006s

10. Testování kolekcí – řezů a map

Podívejme se nyní ve stručnosti, jak lze testovat kolekce. Začneme řezem, u nějž lze otestovat jeho velikost (počet prvků), kapacitu a zda obsahuje zadaný prvek:

package main
 
import (
        . "github.com/onsi/gomega"
        "testing"
)
 
func TestCollecions(t *testing.T) {
        g := NewGomegaWithT(t)
        c := []int{1, 2, 3, 4, 5}
        g.Expect(c).To(HaveLen(5))
        g.Expect(c).To(HaveCap(5))
        g.Expect(c).To(ContainElement(1))
        g.Expect(c).To(Not(ContainElement(42)))
}

Podobně můžeme testovat mapy (například výsledky nějaké funkce). Kromě výše uvedených testů je možné zjistit, zda mapa obsahuje nějaký specifikovaný klíč či nikoli:

package main
 
import (
        . "github.com/onsi/gomega"
        "testing"
)
 
func TestCollecions(t *testing.T) {
        g := NewGomegaWithT(t)
 
        m := make(map[string]int)
        m["one"] = 1
        m["two"] = 2
        m["three"] = 3
 
        g.Expect(m).To(HaveLen(3))
        g.Expect(m).To(ContainElement(1))
        g.Expect(m).To(Not(ContainElement(42)))
        g.Expect(m).To(HaveKey("two"))
        g.Expect(m).To(Not(HaveKey("fourty two")))
}

11. Framework Ginkgo

Ve druhé části dnešního článku se ve stručnosti seznámíme s frameworkem nazvaným Ginkgo, což je nástroj sloužící pro vytváření testů ve stylu BDD (Behavior Driven Development), tedy testů popisujících očekávané chování systému. Tento framework se velmi často kombinuje právě s knihovnou Gomega popsanou v předchozích kapitolách.

Obrázek 2: Logo frameworku Ginkgo

12. Příprava projektu pro otestování s využitím frameworku Ginkgo

Pro porovnání s předchozími demonstračními příklady budeme opět testovat funkci pro výpočet faktoriálu. Tentokrát ovšem bude tato funkce umístěna v balíčku nazvaném factorial:

package factorial
 
func Factorial(n int64) int64 {
        switch {
        case n < 0:
                return 1
        case n == 0:
                return 1
        default:
                return n * Factorial(n-1)
        }
}

Z balíčku, který se nyní skládá z jediného souboru, vytvoříme modul, a to konkrétně příkazem:

$ go mod init factorial

Po spuštění předchozího příkazu by měl vzniknout soubor nazvaný „go.mod“ s následujícím obsahem:

module factorial
 
go 1.13
Poznámka: konkrétní verze Go se pochopitelně může lišit.

13. Vygenerování kostry sady BDD testů

Následně je nutné vytvořit kostru BDD testů. Ve skutečnosti je to velmi snadné, protože tuto práci za nás odvede přímo framework Ginkgo. Postačuje pouze použít příkaz:

$ ginkgo bootstrap

Po spuštění předchozího příkazu by měl vzniknout nový soubor nazvaný „factorial_suite_test.go“, jehož obsah je následující:

package factorial_test
 
import (
        "testing"

        . "github.com/onsi/ginkgo"
        . "github.com/onsi/gomega"
)
 
func TestFactorial(t *testing.T) {
        RegisterFailHandler(Fail)
        RunSpecs(t, "Factorial Suite")
}

Po prvním spuštění testů příkazem:

$ ginkgo

By mělo dojít k úpravě obsahu souboru go.mod:

module factorial
 
go 1.13
 
require (
        github.com/onsi/ginkgo v1.10.3
        github.com/onsi/gomega v1.7.1
)

I souboru go.sum (povšimněte si mnoha závislostí):

github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

14. Vytvoření kostry BDD testu

Následně je nutné vytvořit kostru BDD testu. K tomuto účelu slouží opět nástroj ginkgo, tentokrát ovšem s přepínačem generate, za nímž následuje jméno testu:

$ ginkgo generate factorial

Tento příkaz by měl vygenerovat soubor pojmenovaný „factorial_test.go“ s tímto obsahem:

package factorial_test
 
import (
        . "github.com/onsi/ginkgo"
        . "github.com/onsi/gomega"

        . "factorial"
)
 
var _ = Describe("Factorial", func() {
 
})

Tento test, i když ve skutečnosti neobsahuje žádné podmínky, můžeme po zakomentování nepoužitých importů spustit:

$ ginkgo
 
Running Suite: Factorial Suite
==============================
Random Seed: 1575486131
Will run 0 of 0 specs
 
 
Ran 0 of 0 Specs in 0.000 seconds
SUCCESS! -- 0 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS
 
Ginkgo ran 1 suite in 670.848991ms

Obrázek 3: Barevná podoba výstupu.

15. Implementace BDD testu

Samotné kroky BDD testu se zapisují do volání funkce Describe, a to konkrétně ve tvaru vnořených anonymních funkcí. První anonymní funkce vytvoří kontext, v němž budou vybrané testy probíhat a jednotlivé konkrétní kroky jsou pak reprezentovány anonymní funkcí uvnitř It. Celek tak do značné míry připomíná Mocha framework ze světa JavaScriptu a TypeScriptu:

package factorial_test
 
import (
        . "github.com/onsi/ginkgo"
        . "github.com/onsi/gomega"
 
        . "factorial"
)
 
var _ = Describe("Factorial", func() {
        Context("For zero input", func() {
                It("should be one", func() {
                        Expect(Factorial(0)).To(Equal(int64(1)))
                })
        })
})

Tento prozatím jednoduchý test můžeme opět spustit. Tentokrát by se měla vypsat informace o jednom úspěšně dokončeném testu:

$ ginkgo
 
Running Suite: Factorial Suite
==============================
Random Seed: 1575486467
Will run 1 of 1 specs
 
•
Ran 1 of 1 Specs in 0.000 seconds
SUCCESS! -- 1 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS
 
Ginkgo ran 1 suite in 710.057394ms
Test Suite Passed

Obrázek 4: Barevná podoba výstupu.

16. Rozdělení složitějšího BDD testu do bloků

Rozsáhlejší testovací scénáře je vhodné rozdělit do několika bloků s odděleným kontextem. V našem konkrétním případě lze testovat výpočet faktoriálů negativních čísel, dále pak výpočet faktoriálu nuly a nakonec výpočet faktoriálu pro kladné vstupy:

package factorial_test
 
import (
        . "github.com/onsi/ginkgo"
        . "github.com/onsi/gomega"

        . "factorial"
)
 
var _ = Describe("Factorial", func() {
        Context("For negative input", func() {
                It("should be one", func() {
                        Expect(Factorial(-1)).To(Equal(int64(1)))
                        Expect(Factorial(-10)).To(Equal(int64(1)))
                })
        })
        Context("For zero input", func() {
                It("should be one", func() {
                        Expect(Factorial(0)).To(Equal(int64(1)))
                })
        })
        Context("For positive input", func() {
                It("should be n!", func() {
                        Expect(Factorial(1)).To(Equal(int64(1)))
                        Expect(Factorial(2)).To(Equal(int64(2)))
                        Expect(Factorial(9)).To(Equal(int64(362880)))
                        Expect(Factorial(10)).To(Equal(int64(3628800)))
                })
        })
})

Výsledek takto strukturovaných testů:

$ ginkgo
 
Running Suite: Factorial Suite
==============================
Random Seed: 1575486790
Will run 3 of 3 specs
 
•••
Ran 3 of 3 Specs in 0.000 seconds
SUCCESS! -- 3 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS
 
Ginkgo ran 1 suite in 697.181456ms
Test Suite Passed

Obrázek 5: Barevná podoba výstupu.

17. Podrobnější výpis průběhu testů

V případě, že je zapotřebí vypsat podrobnější informace o probíhajících testech, lze použít přepínač -v (verbose) s následujícím účinkem:

Obrázek 6: Podrobnější výpis průběhu testů.

18. Chování frameworku Ginkgo při chybě

Pokud schválně poškodíme testovanou funkci tak, aby vracela nekorektní výsledky, dojde pochopitelně k detekci této anomálie přímo v testech:

package factorial
 
func Factorial(n int64) int64 {
        switch {
        case n < 0:
                return 1
        case n == 0:
                return 1
        default:
                return n * Factorial(n-2)
        }
}

V tomto případě Ginkgo společně s knihovnou Gomega ohlásí přesně, k jakému problému došlo a na jakém řádku testu:

$ ginkgo
 
Running Suite: Factorial Suite
==============================
Random Seed: 1575487214
Will run 3 of 3 specs
 
••
------------------------------
• Failure [0.000 seconds]
Factorial
/home/ptisnovs/src/go-root/article_45/iteration7/factorial_test.go:10
  For positive input
  /home/ptisnovs/src/go-root/article_45/iteration7/factorial_test.go:22
    should be n! [It]
    /home/ptisnovs/src/go-root/article_45/iteration7/factorial_test.go:23
 
    Expected
        <int64>: 945
    to equal
        <int64>: 362880
 
    /home/ptisnovs/src/go-root/article_45/iteration7/factorial_test.go:26
------------------------------
 
 
Summarizing 1 Failure:
 
[Fail] Factorial For positive input [It] should be n!
/home/ptisnovs/src/go-root/article_45/iteration7/factorial_test.go:26
 
Ran 3 of 3 Specs in 0.001 seconds
FAIL! -- 2 Passed | 1 Failed | 0 Pending | 0 Skipped
--- FAIL: TestFactorial (0.00s)
FAIL
 
Ginkgo ran 1 suite in 697.239929ms
Test Suite Failed

Obrázek 7: Podrobnější výpis průběhu testů.

19. Repositář s demonstračními příklady

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně pět až šest megabajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Příklad Stručný popis Cesta
1 iteration1/ kostra projektu, který má být testován https://github.com/tisnik/go-root/blob/master/article45/i­teration1/
2 iteration2 projekt s inicializovaným systémem modulů https://github.com/tisnik/go-root/blob/master/article45/i­teration2/
3 iteration3 vygenerovaná kostra sady testů projektem Ginkgo https://github.com/tisnik/go-root/blob/master/article45/i­teration3/
4 iteration4 vytvořená kostra BDD testu projektem Ginkgo https://github.com/tisnik/go-root/blob/master/article45/i­teration4/
5 iteration5 první skutečný BDD test https://github.com/tisnik/go-root/blob/master/article45/i­teration5/
6 iteration6 sada tří BDD testů https://github.com/tisnik/go-root/blob/master/article45/i­teration6/
7 iteration7 testy, které zhavarují kvůli špatně napsané funkci https://github.com/tisnik/go-root/blob/master/article45/i­teration7/
       
8 factorial1 funkce pro výpočet faktoriálu https://github.com/tisnik/go-root/blob/master/article45/fac­torial1
9 factorial2 základní testy postavené nad Gomega https://github.com/tisnik/go-root/blob/master/article45/fac­torial2
10 factorial2_omega alternativní způsob zápisu testů https://github.com/tisnik/go-root/blob/master/article45/fac­torial2_omega
11 factorial3 porovnání numerických hodnot a použití klauzule SatisfyAll https://github.com/tisnik/go-root/blob/master/article45/fac­torial3
12 factorial4 rozšíření jednotkových testů o podmínku, zda došlo či naopak nedošlo k chybě https://github.com/tisnik/go-root/blob/master/article45/fac­torial4
13 factorial5 zprávy vypsané ve chvíli, kdy je výpočet nekorektní https://github.com/tisnik/go-root/blob/master/article45/fac­torial5
       
14 collections testování vlastností řezu (slice) https://github.com/tisnik/go-root/blob/master/article45/co­llections
15 maps testování vlastností mapy https://github.com/tisnik/go-root/blob/master/article45/maps

20. Odkazy na Internetu

  1. Ginkgo
    http://onsi.github.io/ginkgo/
  2. Gomega
    https://onsi.github.io/gomega/
  3. Ginkgo's Preferred Matcher Library na GitHubu
    https://github.com/onsi/gomega/
  4. Provided Matchers
    http://onsi.github.io/gomega/#provided-matchers
  5. Dokumentace k balíčku goexpect
    https://godoc.org/github.com/go­ogle/goexpect
  6. Balíček goexpect
    https://github.com/google/goexpect
  7. Balíček go-expect
    https://github.com/Netflix/go-expect
  8. Balíček gexpect
    https://github.com/Thomas­Rooney/gexpect
  9. Expect (originál naprogramovaný v TCL)
    https://core.tcl-lang.org/expect/index
  10. Expect (Wikipedia)
    https://en.wikipedia.org/wiki/Expect
  11. Pexpect
    https://pexpect.readthedoc­s.io/en/stable/
  12. Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
    http://networkbit.ch/golang-ssh-client/
  13. goblin na GitHubu
    https://github.com/franela/goblin
  14. Mocha framework
    https://mochajs.org/
  15. frisby na GitHubu
    https://github.com/verdverm/frisby
  16. package frisby
    https://godoc.org/github.com/ver­dverm/frisby
  17. Frisby alternatives and similar packages (generováno)
    https://go.libhunt.com/frisby-alternatives
  18. Cucumber for golang
    https://github.com/DATA-DOG/godog
  19. How to Use Godog for Behavior-driven Development in Go
    https://semaphoreci.com/com­munity/tutorials/how-to-use-godog-for-behavior-driven-development-in-go
  20. Comparative Analysis Of GoLang Testing Frameworks
    https://www.slideshare.net/Dushy­antBhalgami/comparative-analysis-of-golang-testing-frameworks
  21. A Quick Guide to Testing in Golang
    https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/
  22. Tom's Obvious, Minimal Language.
    https://github.com/toml-lang/toml
  23. xml.org
    http://www.xml.org/
  24. Soubory .properties
    https://en.wikipedia.org/wi­ki/.properties
  25. Soubory INI
    https://en.wikipedia.org/wi­ki/INI_file
  26. JSON to YAML
    https://www.json2yaml.com/
  27. Data Format Converter
    https://toolkit.site/format.html
  28. Viper na GitHubu
    https://github.com/spf13/viper
  29. GoDotEnv na GitHubu
    https://github.com/joho/godotenv
  30. The fantastic ORM library for Golang
    http://gorm.io/
  31. Dokumentace k balíčku gorilla/mux
    https://godoc.org/github.com/go­rilla/mux
  32. Gorilla web toolkitk
    http://www.gorillatoolkit.org/
  33. Metric types
    https://prometheus.io/doc­s/concepts/metric_types/
  34. Histograms with Prometheus: A Tale of Woe
    http://linuxczar.net/blog/2017/06/15/pro­metheus-histogram-2/
  35. Why are Prometheus histograms cumulative?
    https://www.robustperception.io/why-are-prometheus-histograms-cumulative
  36. Histograms and summaries
    https://prometheus.io/doc­s/practices/histograms/
  37. Instrumenting Golang server in 5 min
    https://medium.com/@gsisi­mogang/instrumenting-golang-server-in-5-min-c1c32489add3
  38. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  39. Sémantické verzování
    https://semver.org/
  40. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  41. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  42. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  43. Modules
    https://github.com/golang/go/wi­ki/Modules
  44. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  45. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  46. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  47. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  48. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  49. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  50. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  51. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  52. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  53. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  54. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  55. gopls
    https://github.com/golang/go/wi­ki/gopls
  56. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  57. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  58. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  59. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  60. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  61. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  62. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  63. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  64. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  65. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  66. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  67. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  68. Package trace
    https://golang.org/pkg/runtime/trace/
  69. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  70. Command trace
    https://golang.org/cmd/trace/
  71. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  72. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  73. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  74. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  75. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  76. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  77. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  78. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  79. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  80. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  81. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  82. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  83. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  84. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  85. The LLDB Debugger
    http://lldb.llvm.org/
  86. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  87. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  88. Go is on a Trajectory to Become the Next Enterprise Programming Language
    https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e
  89. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  90. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  91. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  92. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  93. 10 tools written in Go that every developer needs to know
    https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/
  94. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  95. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  96. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  97. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  98. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  99. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  100. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  101. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  102. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  103. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  104. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  105. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  106. go-cron
    https://github.com/rk/go-cron
  107. gocron
    https://github.com/jasonlvhit/gocron
  108. clockwork
    https://github.com/whiteShtef/cloc­kwork
  109. clockwerk
    https://github.com/onatm/clockwerk
  110. JobRunner
    https://github.com/bamzi/jobrunner
  111. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  112. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  113. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  114. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  115. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  116. go-prompt
    https://github.com/c-bata/go-prompt
  117. readline
    https://github.com/chzyer/readline
  118. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  119. go-readline
    https://github.com/fiorix/go-readline
  120. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  121. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  122. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  123. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  124. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  125. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  126. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  127. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  128. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  129. Editline Library (libedit)
    http://thrysoee.dk/editline/
  130. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  131. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  132. WinEditLine
    http://mingweditline.sourceforge.net/
  133. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  134. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  135. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  136. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  137. history(3) – Linux man page
    https://linux.die.net/man/3/history
  138. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  139. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  140. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  141. Balíček ogletest
    https://github.com/jacobsa/ogletest
  142. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  143. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  144. package testing
    https://golang.org/pkg/testing/
  145. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  146. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  147. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  148. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  149. GoConvey
    http://goconvey.co/
  150. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  151. 5 simple tips and tricks for writing unit tests in #golang
    https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742
  152. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  153. package gg
    https://godoc.org/github.com/fo­gleman/gg
  154. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  155. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  156. The Go image package
    https://blog.golang.org/go-image-package
  157. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  158. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  159. YAML
    https://yaml.org/
  160. edn
    https://github.com/edn-format/edn
  161. Smile
    https://github.com/FasterXML/smile-format-specification
  162. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  163. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  164. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  165. Introducing JSON
    http://json.org/
  166. Package json
    https://golang.org/pkg/encoding/json/
  167. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  168. Go by Example: JSON
    https://gobyexample.com/json
  169. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  170. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  171. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  172. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  173. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  174. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  175. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  176. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  177. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  178. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  179. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  180. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  181. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  182. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  183. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  184. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  185. Algorithms to Go
    https://yourbasic.org/
  186. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  187. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/
  188. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  189. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  190. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  191. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  192. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  193. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  194. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  195. The Go Programming Language (home page)
    https://golang.org/
  196. GoDoc
    https://godoc.org/
  197. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  198. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  199. The Go Programming Language Specification
    https://golang.org/ref/spec
  200. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  201. Package builtin
    https://golang.org/pkg/builtin/
  202. Package fmt
    https://golang.org/pkg/fmt/
  203. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  204. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  205. Learning Go
    https://www.miek.nl/go/
  206. Go Bootcamp
    http://www.golangbootcamp.com/
  207. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  208. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  209. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  210. The Go Blog
    https://blog.golang.org/
  211. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  212. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  213. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  214. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  215. How the Go runtime implements maps efficiently (without generics)
    https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics
  216. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  217. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  218. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  219. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  220. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  221. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  222. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  223. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  224. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  225. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  226. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  227. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  228. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  229. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  230. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  231. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  232. Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
    https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/
  233. 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
    https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd
  234. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  235. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  236. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  237. Go vs. Python
    https://www.peterbe.com/plog/govspy
  238. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  239. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  240. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  241. Go by Example: Slices
    https://gobyexample.com/slices
  242. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  243. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  244. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  245. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  246. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  247. nils In Go
    https://go101.org/article/nil.html
  248. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  249. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  250. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  251. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  252. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  253. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  254. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  255. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  256. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  257. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  258. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  259. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  260. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  261. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  262. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  263. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  264. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  265. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  266. Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
    https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06
  267. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  268. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  269. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  270. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  271. Selectors
    https://golang.org/ref/spec#Selectors
  272. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  273. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  274. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  275. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  276. Part 21: Goroutines
    https://golangbot.com/goroutines/
  277. Part 22: Channels
    https://golangbot.com/channels/
  278. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  279. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  280. Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
    https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/
  281. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  282. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  283. Control Structures
    https://www.golang-book.com/books/intro/5
  284. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  285. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  286. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  287. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  288. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  289. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  290. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  291. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  292. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  293. Effective Go
    https://golang.org/doc/ef­fective_go.html
  294. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  295. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  296. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  297. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  298. Nils in Go
    https://www.doxsey.net/blog/nils-in-go