Obsah
1. Testování aplikací zapisujících informace na standardní výstup
2. Princip zachycení standardního výstupu
3. Postup při zachycení standardního výstupu
4. Implementace jednotlivých bodů z popsaného postupu
5. Úplný zdrojový kód funkce pro zachycení standardního výstupu
6. Otestování funkce pro zachycení standardního výstupu
7. Zachycení tisku na standardní výstup v jednotkových testech
8. Zachycení tisku do chybového výstupu
10. Implementace zachycení tisku do logů
11. Testování handlerů implementovaných v HTTP serveru
13. Implementace testu handleru HTTP serveru
14. Pokrytí kódu HTTP serveru testy
15. Repositář s demonstračními příklady
1. Testování aplikací zapisujících informace na standardní výstup
Při tvorbě testů pro ty aplikace, které zapisují informace na standardní (popř. na chybový) výstup se můžeme setkat s požadavkem, aby se zkontrolovalo, zda testovaná aplikace skutečně na standardní nebo chybový výstup zapsala očekávané zprávy. K této problematice je možné přistoupit několika způsoby. Jedno z možných (i když mnohdy ne ideálních) řešení spočívá v použití některé ze specializovaných knihoven, s nimiž jsme se již v tomto seriálu setkali. Jedná se především o knihovny s podobnými názvy go-expect, goexpect a gexpect, které byly popsány v následujících dvou článcích:
- Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem
https://www.root.cz/clanky/pouziti-go-pro-automatizaci-prace-s-aplikacemi-s-interaktivnim-prikazovym-radkem/ - Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem (dokončení)
https://www.root.cz/clanky/pouziti-go-pro-automatizaci-prace-s-aplikacemi-s-interaktivnim-prikazovym-radkem-dokonceni/
Jen pro úplnost si ukažme jeden příklad použití těchto knihoven (který jsme si již v tomto seriálu taktéž uvedli). Jedná se o krátký program, který spustí interpret programovacího jazyka Python, počká na inicializaci interpretru a nakonec nechá tímto interpretrem vyhodnotit několik výrazů, pochopitelně s testem, zda jsme získali očekávané výsledky. Nakonec je Python ukončen standardním způsobem:
func expectOutput(child *gexpect.ExpectSubprocess, output string) { err := child.Expect(output) if err != nil { log.Fatal(err) } } func expectPrompt(child *gexpect.ExpectSubprocess) { expectOutput(child, ">>> ") } func sendCommand(child *gexpect.ExpectSubprocess, command string) { err := child.SendLine(command) if err != nil { log.Fatal(err) } } func main() { child, err := gexpect.Spawn("python") if err != nil { log.Fatal(err) } expectPrompt(child) sendCommand(child, "1+2") expectOutput(child, "3") expectPrompt(child) sendCommand(child, "6*7") expectOutput(child, "42") expectPrompt(child) sendCommand(child, "quit()") child.Wait() }
Výše zmíněné knihovny jsou však primárně určeny pro psaní testů, popř. automatizačních skriptů, které většinou s aplikací pracují jako s black boxem. Ovšem mnohdy potřebujeme zjistit, jaké informace se na výstup zapsaly přímo v jednotkových testech (unit tests). A právě v takových případech lze využít postupu, jenž je popsán a ukázán v navazujících kapitolách.
2. Princip zachycení standardního výstupu
Vzhledem k tomu, že všechny funkce ze standardní knihovny, které jsou určeny pro výpis informací na standardní výstup (jedná se o funkce z balíčku fmt), jsou skutečně implementovány jako běžné funkce a nikoli jako metody, je v tomto případě relativně obtížné při psaní jednotkových testů použít mockování (které se v programovacím jazyce Go provádí poněkud obtížněji, než například v Pythonu). Můžeme však namísto toho změnit obsah proměnné os.Stdout a nahradit ho jiným vhodným objektem, přesněji řečeno takovým objektem, který je odvozen od struktury os.File.
Ve zdrojových kódech balíčku os lze najít, jakého typu (a hodnoty) je proměnná os.Stdout a taktéž příbuzná proměnná os.Stderr:
var ( Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin") Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout") Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr") )
Jakmile se obsah této proměnné změní, například když do ní přiřadíme jiný otevřený soubor, budou všechny standardní funkce pro tisk (tedy funkce z balíčku fmt) používat nový cíl – odlišný soubor či strukturu, která chování souboru napodobuje. Je tomu tak z toho důvodu, že tyto funkce vypadají následovně:
func Print(a ...interface{}) (n int, err error) { return Fprint(os.Stdout, a...) } func Println(a ...interface{}) (n int, err error) { return Fprintln(os.Stdout, a...) } func Printf(format string, a ...interface{}) (n int, err error) { return Fprintf(os.Stdout, format, a...) }
Ovšem řešení založené na tom, že se namísto standardního výstupu použije výstup do souboru, pochopitelně není ideální. Výhodnější by bylo, aby se tisk zprávy či zpráv provedl do k tomu alokované paměťové oblasti, tedy do nějakého bufferu, jehož obsah by bylo možné následně zkontrolovat a zjistit tak, jaké zprávy aplikace v dané (testované) funkci vytiskla. Právě pro tento účel lze použít strukturu, s níž jsme se již v tomto seriálu dobře seznámili – pipe:
$ go doc os.Pipe package os // import "os" func Pipe() (r *File, w *File, err error) Pipe returns a connected pair of Files; reads from r return bytes written to w. It returns the files and an error, if any.
Díky tomu, že do pipe může aplikace zapisovat (jakoby se jednalo o standardní výstup) a současně je možné data z druhé strany přečíst a zpracovat, může pipe posloužit právě ve funkci bufferu pro zapamatování zpráv, které aplikace zapsala na standardní výstup.
3. Postup při zachycení standardního výstupu
Celý postup při zachycování standardního výstupu může ve zkrácené podobě vypadat následovně:
- Zapamatujeme si původní obsah proměnné os.Stdout, protože ho budeme chtít po testování obnovit.
- Vytvoříme nový objekt typu Pipe, což mj. znamená, že získáme i implementace readeru a writeru (první dvě návratové hodnoty jsou typu *File).
- Do proměnné os.Stdout přiřadíme writer, tj. vstupní část objektu typu Pipe (část, do které se provádí zápis – tisk zpráv).
- V gorutině použijeme nějakou funkci, která přečte celý obsah pipe (použije výstupní část, tedy reader, s čekáním na dokončení zápisu) a převede ji na řetězec.
- Dále se zavolá testovaná funkce, která může provádět zápisy zpráv (tedy tisk) na standardní výstup.
- Na konci již pouze stačí pipe uzavřít (tím dojde k dokončení gorutiny, která svůj výsledek zapíše do kanálu), obnovit obsah proměnné os.Stdout a vrátit řetězec, který byl zachycen.
Vlastní implementace je ve skutečnosti nepatrně složitější, a to zejména kvůli tomu, že gorutina určená pro zpracování zpráv zapisovaných na standardní výstup musí komunikovat s původní gorutinou, ve které (mj.) běží i testovaná funkce. A nakonec je vhodné počkat na to, až se nově vytvořená gorutina spustí. K tomuto účelu lze použít například nějakou formu synchronizačního objektu.
4. Implementace jednotlivých bodů z popsaného postupu
Pomocná funkce, která vrátí řetězec vytisknutý na standardní výstup libovolnou předanou funkcí, může mít tuto hlavičku:
func CaptureStandardOutput(function func()) (string, error) { }
Funkce tedy akceptuje libovolnou (typicky anonymní) testovanou funkci a na konci vrátí zachycený řetězec a popř. i objekt nesoucí informace o chybě, která v průběhu zachycování nastala.
V předchozí kapitole popsaný postup pro zachycení tisku na standardním výstupu, přesněji řečeno jeho jednotlivé body, lze realizovat například následujícím způsobem.
1. Zapamatování původního obsahu proměnné os.Stdout
Toto je velmi jednoduše implementovatelný bod. Postačuje nám do lokální proměnné uložit aktuální obsah proměnné os.Stdout, nezávisle na tom, zda se skutečně jedná o klasický standardní výstup, nebo o již dříve provedené přesměrování do souboru:
stdout := os.Stdout
2. Vytvoření objektu typu Pipe
Vytvoření objektu typu Pipe již známe, neboť jsme se tímto tématem již zabývali v předchozích částech tohoto seriálu. Pochopitelně nesmíme zapomenout na otestování chybového stavu, který (teoreticky) může nastat:
reader, writer, err := os.Pipe() if err != nil { return "", err }
Po provedení výše uvedeného bloku kódu máme k dispozici proměnné obsahující ukazatele na dvojici pseudosouborů – jeden je určený pro čtení, druhý pro zápis.
3. Nový obsah proměnné os.Stdout
Opět se jedná o snadno realizovatelný bod, neboť pouze nastavíme novou hodnotu proměnné os.Stdout a navíc zajistíme, aby se při ukončení celé funkce pro zachycení standardního výstupu obnovil původní obsah této proměnné. To je důležité, protože v opačném případě by přestaly pracovat všechny další tisky na standardní výstup (ovšem k chybě by nedošlo, a to i přesto, že se pipe uzavřela):
defer func() { os.Stdout = stdout }() os.Stdout = writer
4. Gorutina, která obsah Pipe přečte a převede na řetězec
V nejjednodušším případě tato asynchronně běžící gorutina přečte obsah Pipe, převede ho na řetězec a následně tento řetězec zapíše do kanálu, který je použit pro komunikaci s touto gorutinou:
captured := make(chan string) go func() { var buf bytes.Buffer io.Copy(&buf, reader) captured <- buf.String() }()
Korektnější je však počkat na to, až se gorutina skutečně spustí:
captured := make(chan string) wg := new(sync.WaitGroup) wg.Add(1) go func() { var buf bytes.Buffer wg.Done() io.Copy(&buf, reader) captured <- buf.String() }() wg.Wait()
V tomto případě je použit synchronizační mechanismus představovaný objektem typu WaitGroup. V hlavní gorutině metodou Add nastavíme, že se má čekat na jedinou další gorutinu (předáme tedy hodnotu 1, kterou se inicializuje interní synchronizované počitadlo). V této gorutině (představované anonymní funkcí) zavoláme metodu Done, ovšem nikoli na konci gorutiny, ale na jejím začátku (ihned po její inicializaci). V hlavní gorutině poté metodou Wait čekáme na inicializaci asynchronně běžící gorutiny.
5. Zavolání testované funkce provádějící tisk na standardní výstup
Toto je jednoduchý bod – zavoláme jakoukoli funkci, která může (ale nutně nemusí) provádět tisk na standardní výstup:
function()
6. Obnovení obsahu proměnné os.Stdout, vrácení zachyceného řetězce
Po zavolání testované funkce uzavřeme Pipe a vrátíme obsah kanálu, do kterého zapsala řetězec (i prázdný) asynchronně běžící gorutina. Současně se čtení kanálu používá pro čekání na dokončení této gorutiny:
writer.Close() return <-captured, nil
5. Úplný zdrojový kód funkce pro zachycení standardního výstupu
Úplný zdrojový kód balíčku s funkcí sloužící pro zachycení standardního výstupu může vypadat následovně:
package main import ( "bytes" "io" "os" "sync" ) func CaptureStandardOutput(function func()) (string, error) { // backup of the real stdout stdout := os.Stdout // temporary replacement for stdout reader, writer, err := os.Pipe() if err != nil { return "", err } // temporarily replace real Stdout by the mocked one defer func() { os.Stdout = stdout }() os.Stdout = writer // channel with captured standard output captured := make(chan string) // synchronization object wg := new(sync.WaitGroup) // we are going to wait for one goroutine only wg.Add(1) go func() { var buf bytes.Buffer // goroutine is started -> inform main one via WaitGroup object wg.Done() io.Copy(&buf, reader) captured <- buf.String() }() // wait for goroutine to start wg.Wait() // provided function that (probably) prints something to standard output function() writer.Close() return <-captured, nil }
6. Otestování funkce pro zachycení standardního výstupu
To, zda výše popsaná funkce CaptureStandardOutput skutečně dokáže zachytit tisk na standardní výstup, lze relativně snadno ověřit.
Nejprve vyzkoušíme zachycení tisku provedeného funkcí fmt.Print:
func main() { str, err := CaptureStandardOutput(func() { fmt.Print("Hello world!") }) if err != nil { panic(err) } fmt.Println("Captured output:") fmt.Println("-------------------------------") fmt.Println(str) fmt.Println("-------------------------------") }
S výsledkem:
Captured output: ------------------------------- Hello world! -------------------------------
Vidíme, že se řetězec „Hello world!“ skutečně zachytil a je na standardní výstup vypsán až mnohem později (v reálných testech by se výpis neprováděl, pouze by se zjistilo, zda řetězec obsahuje potřebné informace).
Podobný příklad, ovšem zachycující tisk funkcí fmt.Println (s odřádkováním), je prakticky totožný s příkladem předchozím:
func main() { str, err := CaptureStandardOutput(func() { fmt.Println("Hello world!") }) if err != nil { panic(err) } fmt.Println("Captured output:") fmt.Println("-------------------------------") fmt.Println(str) fmt.Println("-------------------------------") }
S výsledkem:
Captured output: ------------------------------- Hello world! -------------------------------
Tabulka s vybranými hodnotami funkce sinus:
func printSinus() { epsilon := 1e-6 for x := 0.0; x <= 2.0*math.Pi + epsilon; x+= math.Pi/6.0 { fmt.Printf("sin(%5.2f) = %+5.3f\n", x, math.Sin(x)) } } func main() { str, err := CaptureStandardOutput(printSinus) if err != nil { panic(err) } fmt.Println("Captured output:") fmt.Println("-------------------------------") fmt.Println(str) fmt.Println("-------------------------------") }
S výsledkem:
Captured output: ------------------------------- sin( 0.00) = +0.000 sin( 0.52) = +0.500 sin( 1.05) = +0.866 sin( 1.57) = +1.000 sin( 2.09) = +0.866 sin( 2.62) = +0.500 sin( 3.14) = -0.000 sin( 3.67) = -0.500 sin( 4.19) = -0.866 sin( 4.71) = -1.000 sin( 5.24) = -0.866 sin( 5.76) = -0.500 sin( 6.28) = +0.000 -------------------------------
Tisk do chybového výstupu funkcí println se ovšem nezachytí (což je ostatně korektní, tato základní funkce tiskne na chybový výstup a navíc obchází většinu funkcí z balíčku os):
func main() { str, err := CaptureStandardOutput(func() { println("Error output") }) if err != nil { panic(err) } fmt.Println("Last line of captured output:") fmt.Println("-------------------------------") fmt.Println(str) fmt.Println("-------------------------------") }
S výsledkem:
Error output Captured output: ------------------------------- -------------------------------
Alternativně lze tisk na chybový výstup provést explicitně:
func main() { str, err := CaptureStandardOutput(func() { fmt.Fprintln(os.Stderr, "Error output again") }) if err != nil { panic(err) } fmt.Println("Last line of captured output:") fmt.Println("-------------------------------") fmt.Println(str) fmt.Println("-------------------------------") }
S výsledkem:
Error output again Captured output: ------------------------------- -------------------------------
7. Zachycení tisku na standardní výstup v jednotkových testech
Jak jsme si již řekli v úvodní kapitole, je většinou zapotřebí zachytit tisk na standardní výstup v jednotkových testech. Se znalostmi, které jsme získali v předchozích kapitolách, je to ve skutečnosti velmi snadné. Ostatně se podívejme na následující příklad, v němž je nejdříve zopakována funkce pro zachycení výstupu (ovšem nikoli v balíčku main) a následně je pro tento balíček vytvořen jednoduchý jednotkový test:
package capture import ( "bytes" "io" "os" "sync" ) func StandardOutput(function func()) (string, error) { // backup of the real stdout stdout := os.Stdout // temporary replacement for stdout reader, writer, err := os.Pipe() if err != nil { return "", err } // temporarily replace real Stdout by the mocked one defer func() { os.Stdout = stdout }() os.Stdout = writer // channel with captured standard output captured := make(chan string) // synchronization object wg := new(sync.WaitGroup) // we are going to wait for one goroutine only wg.Add(1) go func() { var buf bytes.Buffer // goroutine is started -> inform main one via WaitGroup object wg.Done() io.Copy(&buf, reader) captured <- buf.String() }() // wait for goroutine to start wg.Wait() // provided function that (probably) prints something to standard output function() writer.Close() return <-captured, nil }
Jednotkový test pro výše vypsaný balíček by mohl vypadat následovně:
package capture_test import ( "fmt" "github.com/tisnik/go-capture" "os" "testing" ) // TestNoOutput checks if empty standard output is captured properly func TestNoOutput(t *testing.T) { captured, err := capture.StandardOutput(func() { }) if err != nil { t.Fatal("Unable to capture standard output", err) } if captured != "" { t.Fatal("Standard should be empty") } } // TestEmptyOutput checks if empty standard output is captured properly func TestEmptyOutput(t *testing.T) { captured, err := capture.StandardOutput(func() { fmt.Print("") }) if err != nil { t.Fatal("Unable to capture standard output", err) } if captured != "" { t.Fatal("Standard should be empty") } } // TestOutputWithoutNewlines checks if standard output created by fmt.Print is captured properly func TestOutputWithoutNewlines(t *testing.T) { captured, err := capture.StandardOutput(func() { fmt.Print("Hello!") }) if err != nil { t.Fatal("Unable to capture standard output", err) } if captured != "Hello!" { t.Fatal("Incorrect output has been captured:", captured) } } // TestOutputWithNewlines checks if standard output created by fmt.Println is captured properly func TestOutputWithNewlines(t *testing.T) { captured, err := capture.StandardOutput(func() { fmt.Println("Hello!") }) if err != nil { t.Fatal("Unable to capture standard output", err) } if captured != "Hello!\n" { t.Fatal("Incorrect output has been captured:", captured) } } // TestOutputToStdErr checks whether output to stderr is captured or not func TestOutputToStdErr(t *testing.T) { captured, err := capture.StandardOutput(func() { fmt.Fprint(os.Stderr, "Hello!") }) if err != nil { t.Fatal("Unable to capture standard output", err) } if captured != "" { t.Fatal("Incorrect output has been captured:", captured) } }
8. Zachycení tisku do chybového výstupu
Po přečtení předchozích kapitol vás pravděpodobně nepřekvapí, že chybový výstup, resp. přesněji řečeno tisk do chybového výstupu, se provede prakticky totožným programovým kódem, takže jen v krátkosti:
func CaptureErrorOutput(function func()) (string, error) { // backup of the real stderr stderr := os.Stderr // temporary replacement for stdout reader, writer, err := os.Pipe() if err != nil { return "", err } // temporarily replace real Stderr by the mocked one defer func() { os.Stderr = stderr }() os.Stderr = writer // channel with captured standard output captured := make(chan string) wg := new(sync.WaitGroup) wg.Add(1) go func() { var buf bytes.Buffer wg.Done() io.Copy(&buf, reader) captured <- buf.String() }() wg.Wait() // provided function that (probably) prints something to standard output function() writer.Close() return <-captured, nil }
Otestování činnosti výše uvedené funkce:
func main() { str, err := CaptureErrorOutput(func() { fmt.Fprintln(os.Stderr, "Error output again") }) if err != nil { panic(err) } fmt.Println("Captured error output:") fmt.Println("-------------------------------") fmt.Println(str) fmt.Println("-------------------------------") }
S výsledky:
Captured error output: ------------------------------- Error output again -------------------------------
9. Zachycení tisku do logů
Nyní si vyzkoušejme, co se stane ve chvíli, kdy se pokusíme zachytit zápis do logu. Ve zdrojových kódech modulu log lze najít informaci o tom, do jakého výstupu se vlastně logy zapisují:
var std = New(os.Stderr, "", LstdFlags)
Ve skutečnosti ovšem nyní pouhá změna hodnoty os.Stderr není dostačující, protože si modul log drží vlastní referenci na chybový výstup. O tom se ostatně můžeme velmi snadno přesvědčit:
func main() { str, err := CaptureErrorOutput(func() { log.Print("log.Print") }) if err != nil { panic(err) } fmt.Println("Captured standard output:") fmt.Println("-------------------------------") fmt.Println(str) fmt.Println("-------------------------------") }
S tímto výsledkem:
2004/08/29 05:48:04 log.Print Captured error output: ------------------------------- -------------------------------
Vidíme, že se funkce log.Print zavolala zcela standardním způsobem, bez ohledu na to, že zachytáváme chybový výstup.
10. Implementace zachycení tisku do logů
Přímý přístup k proměnné log.std nemáme, musíme tedy použít nějaké jiné řešení. K dispozici je funkce log.SetOutput, které lze předat jiný objekt pro realizaci zápisu logu. Problém je zde vlastně jediný – neexistuje rozumná možnost, jak obnovit předchozí hodnotu, protože není k dispozici žádná funkce typu log.GetOutput. Částečné řešení (které obnoví zápis logů do chybového výstupu) může vypadat takto:
func CaptureLog(function func()) (string, error) { // temporary replacement for log output reader, writer, err := os.Pipe() if err != nil { return "", err } // temporarily replace real log output by the mocked one defer func() { log.SetOutput(os.Stderr) }() log.SetOutput(writer) // channel with captured standard output captured := make(chan string) // synchronization object wg := new(sync.WaitGroup) // we are going to wait for one goroutine only wg.Add(1) go func() { var buf bytes.Buffer // goroutine is started -> inform main one via WaitGroup object wg.Done() io.Copy(&buf, reader) captured <- buf.String() }() // wait for goroutine to start wg.Wait() // provided function that (probably) prints something to standard output function() writer.Close() return <-captured, nil }
Otestování by nyní mělo být triviální:
func main() { str, err := CaptureLog(func() { log.Println("Hello world") }) if err != nil { panic(err) } fmt.Println("Captured logs:") fmt.Println("-------------------------------") fmt.Println(str) fmt.Println("-------------------------------") }
S výsledkem:
Captured logs: ------------------------------- 2020/01/15 20:13:31 Hello world -------------------------------
11. Testování handlerů implementovaných v HTTP serveru
Ve druhé části článku se ve stručnosti zmíníme o dalším úkolu, který mnohdy čeká na autory jednotkových testů. Jedná se o nutnost otestování handlerů implementovaných v HTTP/HTTPS serveru (a není žádnou novinkou, že programovací jazyk Go se pro podobné aplikace velmi často používá). Jednotkové testy handlerů by měly do značné míry napodobit chování celého HTTP serveru, tj. mělo by být možné posílat požadavky (request) na mock HTTP serveru, zjišťovat, jaké informace se vrátily v odpovědi (response) atd. Pro tento účel se používá standardní knihovna net/http/httptest. Tato knihovna se skládá ze dvou částí – funkce NewRequest používané pro testování handlerů a datového typu Server, který (společně s příslušnými metodami) můžeme použít pro psaní (nejenom) jednotkových testů. Dnes se budeme zabývat především výše zmíněnou funkcí NewRequest.
12. Jednoduchý HTTP server
Nejprve si ukažme kód HTTP serveru, který budeme chtít testovat. Tento server po svém spuštění poskytuje statické soubory umístěné v aktuálním adresáři a na endpointech /data a /other odpovídá posláním odpovědi s nastaveným typem „application/json“. V obou případech je kód odpovědi 200 OK (a ve skutečnosti druhý handler nevrací validní JSON). Zdrojový kód tohoto HTTP serveru naleznete na adrese https://github.com/tisnik/go-root/blob/master/article52/httpServer1.go
package main import ( "fmt" "log" "net/http" ) func dataHandler(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) fmt.Fprintf(writer, `"x": [1, 2, 3, 4, 5]`) } func otherHandler(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) fmt.Fprintf(writer, `foobar`) } func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("."))) http.HandleFunc("/data", dataHandler) http.HandleFunc("/other", otherHandler) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
13. Implementace testu handleru HTTP serveru
Typickou úlohou je otestování funkcionality jednotlivých handlerů. Realizaci si ukážeme na testu pro handler obsluhující endpoint /data. Nejdříve vytvoříme objekt realizující dotaz provedený HTTP metodou GET:
request, err := http.NewRequest("GET", "/data", nil) if err != nil { t.Fatal(err) }
Dále vytvoříme objekt, který bude zaznamenávat provedené operace:
recorder := httptest.NewRecorder()
Třetím a posledním objektem je adaptér umožňující použít libovolnou funkci s příslušnou signaturou jako handler HTTP serveru:
handler := http.HandlerFunc(dataHandler)
Nyní spustíme „záznam“ činnosti HTTP serveru pro již dříve vytvořený dotaz (HTTP GET na endpointu /data):
handler.ServeHTTP(recorder, request)
Celý průběh se zaznamená, což znamená, že později můžeme činnost handleru prozkoumat čtením atributů struktury recorder.
Otestování HTTP kódu odpovědi (očekáváme 200 OK):
if status := recorder.Code; status != http.StatusOK { t.Errorf("improper status code: got %v instead of %v", status, http.StatusOK) }
Otestování, zda odpověď obsahuje hlavičku „Content-Type“ s očekávaným obsahem „application/json“:
if ctype := recorder.Header().Get("Content-Type"); ctype != "application/json" { t.Errorf("content type header does not match: got %s want %s", ctype, "application/json") }
A pochopitelně můžeme přistupovat i k datům poslaným v těle odpovědi:
body := recorder.Body.String() if body != `"x": [1, 2, 3, 4, 5]` { t.Errorf("wrong response body: %s", body) }
Úplný zdrojový kód jednotkového testu je umístěn na adrese: https://github.com/tisnik/go-root/blob/master/article52/httpServer1_test.go
package main import ( "net/http" "net/http/httptest" "testing" ) func TestDataHandler(t *testing.T) { request, err := http.NewRequest("GET", "/data", nil) if err != nil { t.Fatal(err) } recorder := httptest.NewRecorder() handler := http.HandlerFunc(dataHandler) handler.ServeHTTP(recorder, request) if status := recorder.Code; status != http.StatusOK { t.Errorf("improper status code: got %v instead of %v", status, http.StatusOK) } body := recorder.Body.String() if body != `"x": [1, 2, 3, 4, 5]` { t.Errorf("wrong response body: %s", body) } if ctype := recorder.Header().Get("Content-Type"); ctype != "application/json" { t.Errorf("content type header does not match: got %s want %s", ctype, "application/json") } }
14. Pokrytí kódu HTTP serveru testy
Jednotkové testy spustíme příkazem go test, ovšem navíc budeme specifikovat, že je nutné zjistit pokrytí kódu testy a uložit naměřená data do souboru nazvaného „coverage.out“:
$ go test -coverprofile coverage.out
Dále z vytvořeného souboru „coverage.out“ vytvoříme čitelný výpis s informacemi o tom, jaké funkce HTTP serveru byly skutečně otestovány:
$ go tool cover -func=coverage.out
Výsledek by mohl vypadat následovně (cesty se samozřejmě budou odlišovat):
/home/tester/src/go/httpServer1.go:9: dataHandler 100.0% /home/tester/src/go/httpServer1.go:15: otherHandler 0.0% /home/tester/src/go/httpServer1.go:21: startHttpServer 0.0% /home/tester/src/go/httpServer1.go:29: main 0.0% total: (statements) 25.0%
Vidíme, že handler realizovaný funkcí dataHandler je skutečně plně pokryt testy, na rozdíl od ostatního programového kódu.
15. 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:
16. Odkazy na Internetu
- Golang Capturing log.Println And fmt.Println Output
https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4 - Stránka projektu plotly
https://plot.ly/ - Plotly JavaScript Open Source Graphing Library
https://plot.ly/javascript/ - Domain coloring
https://en.wikipedia.org/wiki/Domain_coloring - Michael Fogleman's projects
https://www.michaelfogleman.com/projects/tagged/graphics/ - Color Graphs of Complex Functions
https://web.archive.org/web/20120511021419/http://w.american.edu/cas/mathstat/lcrone/ComplexPlot.html - A Gallery of Complex Functions
http://wismuth.com/complex/gallery.html - package glot
https://godoc.org/github.com/Arafatk/glot - Gnuplotting: Output terminals
http://www.gnuplotting.org/output-terminals/ - Introducing Glot the plotting library for Golang
https://medium.com/@Arafat./introducing-glot-the-plotting-library-for-golang-3133399948a1 - Introducing Glot the plotting library for Golang
https://blog.gopheracademy.com/advent-2018/introducing-glot/ - Glot is a plotting library for Golang built on top of gnuplot
https://github.com/Arafatk/glot - Example plots (gonum/plot)
https://github.com/gonum/plot/wiki/Example-plots - A repository for plotting and visualizing data (gonum/plot)
https://github.com/gonum/plot - golang library to make https://chartjs.org/ plots
https://github.com/brentp/go-chartjs - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - The Gonum Numerical Computing Package
https://www.gonum.org/post/introtogonum/ - Gomacro na GitHubu
https://github.com/cosmos72/gomacro - gophernotes – Use Go in Jupyter notebooks and nteract
https://github.com/gopherdata/gophernotes - gonum
https://github.com/gonum - go-gota/gota – DataFrames and data wrangling in Go (Golang)
https://porter.io/github.com/go-gota/gota - A repository for plotting and visualizing data
https://github.com/gonum/plot - Gonum Numerical Packages
https://www.gonum.org/ - Stránky projektu MinIO
https://min.io/ - MinIO Quickstart Guide
https://docs.min.io/docs/minio-quickstart-guide.html - MinIO Go Client API Reference
https://docs.min.io/docs/golang-client-api-reference - MinIO Python Client API Reference
https://docs.min.io/docs/python-client-api-reference.html - Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/ - Benchmarking MinIO vs. AWS S3 for Apache Spark
https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/ - MinIO Client Quickstart Guide
https://docs.min.io/docs/minio-client-quickstart-guide.html - Analýza kvality zdrojových kódů Minia
https://goreportcard.com/report/github.com/minio/minio - This is MinIO
https://www.youtube.com/watch?v=vF0lQh0XOCs - Running MinIO Standalone
https://www.youtube.com/watch?v=dIQsPCHvHoM - „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
https://www.youtube.com/watch?v=wlpn8K0jJ4U - Ginkgo
http://onsi.github.io/ginkgo/ - Gomega
https://onsi.github.io/gomega/ - Ginkgo's Preferred Matcher Library na GitHubu
https://github.com/onsi/gomega/ - Provided Matchers
http://onsi.github.io/gomega/#provided-matchers - Dokumentace k balíčku goexpect
https://godoc.org/github.com/google/goexpect - Balíček goexpect
https://github.com/google/goexpect - Balíček go-expect
https://github.com/Netflix/go-expect - Balíček gexpect
https://github.com/ThomasRooney/gexpect - Expect (originál naprogramovaný v TCL)
https://core.tcl-lang.org/expect/index - Expect (Wikipedia)
https://en.wikipedia.org/wiki/Expect - Pexpect
https://pexpect.readthedocs.io/en/stable/ - Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
http://networkbit.ch/golang-ssh-client/ - goblin na GitHubu
https://github.com/franela/goblin - Mocha framework
https://mochajs.org/ - frisby na GitHubu
https://github.com/verdverm/frisby - package frisby
https://godoc.org/github.com/verdverm/frisby - Frisby alternatives and similar packages (generováno)
https://go.libhunt.com/frisby-alternatives - Cucumber for golang
https://github.com/DATA-DOG/godog - How to Use Godog for Behavior-driven Development in Go
https://semaphoreci.com/community/tutorials/how-to-use-godog-for-behavior-driven-development-in-go - Comparative Analysis Of GoLang Testing Frameworks
https://www.slideshare.net/DushyantBhalgami/comparative-analysis-of-golang-testing-frameworks - A Quick Guide to Testing in Golang
https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/ - Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - 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/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - 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 - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 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/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 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 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - 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/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - 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 - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - 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/ - 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 - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - 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 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - 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/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go