Obsah
1. Knihovna Goblin: zjednodušení testů v programovacím jazyku Go
2. Jednoduché testy vytvořené s využitím knihovny Goblin
3. Testy se složitější strukturou
5. Větší množství funkcí s implementací testů
7. Testování aplikací s REST API s využitím knihovny Frisby
8. Otestování koncových bodů REST API přístupných přes HTTP metodu GET
9. Chování Frisby ve chvíli, kdy není splněna testovaná podmínka
10. Kontrola hlaviček odpovědí poslaných serverem
12. Poslání dat ve formátu JSON na server
13. Kontrola obsahu odpovědi serveru
16. Využití knihovny Frisby společně s knihovnou Testing
17. Padající jednotkový test založený na knihovně Frisby
18. Jednotkové testy s více funkcemi
19. Repositář s demonstračními příklady
1. Knihovna Goblin: zjednodušení testů v programovacím jazyku Go
První knihovnou určenou pro tvorbu čitelných a současně i snadno upravitelných a pochopitelných testů, s níž se dnes seznámíme, je knihovna nazvaná Goblin. Zdrojové kódy i dokumentace k této knihovně jsou dostupné na adrese github.com/franela/goblin. Tato knihovna je do značné míry inspirována známým testovacím frameworkem Mocha, který se používá se světě JavaScriptu a taktéž TypeScriptu. Při použití frameworku Mocha se testy zapisují s využitím funkcí describe a it (volání těchto funkcí lze v případě potřeby vnořovat), přičemž pro samotné testování podmínek je možné použít prakticky libovolnou další knihovnu s implementací asercí, které vyhazují výjimku Error (expects.js, should.js, assert z Node.js atd. atd.).
Obrázek 1: Logo frameworku Mocha, kterým se inspirovali tvůrci knihovny Goblin.
Jednoduchý test, který kontroluje chování metody indexOf pro prvek, který v poli neexistuje, je možné zapsat následujícím způsobem (viz též popis chování):
var assert = require('assert'); describe('Array', function() { describe('#indexOf()', function() { it('should return -1 when the value is not present', function() { assert.equal([1, 2, 3].indexOf(4), -1); }); }); });
Povšimněte si použití funkcí describe pro popis testů i pro určení jejich struktury (není plochá, může se jednat o libovolně strukturovaný strom) a pro zápis samotného testu. V knihovně Mocha se tedy velmi často setkáme s použitím anonymních funkcí a totéž platí i pro knihovnu Goblin popsanou níže.
var assert = require('assert'); describe('Array', () => { describe('#indexOf()', () => { it('should return -1 when the value is not present', function() { assert.equal([1, 2, 3].indexOf(4), -1); }); }); });
Vraťme se nyní ke knihovně Goblin, kterou lze použít v programovacím jazyku Go ke zjednodušení zápisu jednotkových testů popř. v případě potřeby i testů integračních. Samotná knihovna Goblin se v takovém případě používá jako doplněk k základní knihovně Testing, s níž jsme se již v seriálu o programovacím jazyce Go setkali. Připomeňme si, že základní knihovna Testing vývojářům neposkytuje žádné funkce pro aserce, takže veškerý zápis podmínek musí být proveden explicitně, s případným zavoláním metody testing.T.Error() nebo testing.T.Error() (popř. dalších alternativ) ve chvíli, kdy není podmínka splněna:
package main import "testing" func TestAdd(t *testing.T) { result := add(1, 2) if result != 3 { t.Error("1+2 should be 3, got ", result, "instead") } }
Výše uvedeným způsobem je pochopitelně možné zapsat i složitější testy, ovšem jedná se o poměrně nízkoúrovňový přístup, který s sebou přináší několik nevýhod. Především se – kvůli neexistenci asercí – celý test skládá z mnoha explicitně zapisovaných podmínek (ostatně jazyk Go je jedním z mála moderních programovacích jazyků, v němž není konstrukce assert přímo součástí vlastního jazyka). Navíc mají testy jen jednorozměrnou strukturu, bez přidané hierarchie (tu mnohdy musíme doplnit sami vhodným pojmenováním funkcí představujících jednotlivé testy). A konečně – standardní knihovna Testing neposkytuje vývojářům možnost explicitně zapsat informaci o tom, jaká vlastnost se právě testuje. A navíc mnohdy potřebujeme dopředu vytvořit alespoň kostru testů, ovšem bez podrobnější implementace, popř. je nutné některé testy zakázat (vyloučit z testovacího scénáře). Tyto informace je samozřejmě možné přidat ve formě poznámky popř. do případných chybových hlášení, ovšem nejedná se o součást definice testu (lze použít volání testing.T.Skip()). A právě tato omezení knihovny Testing jsou v uspokojivé míře „napravena“ knihovnou Goblin, která možnosti základní knihovny rozšiřuje.
Obrázek 2: Logo projektu Goblin.
2. Jednoduché testy vytvořené s využitím knihovny Goblin
Podívejme se nyní na způsob zápisu jednoduchých testů založených na kombinaci standardní knihovny Testing s knihovnou Goblin. Samotné funkce představující testy budou mít stejnou signaturu, kterou očekává knihovna Testing. To v praxi znamená, že každá funkce s implementací testu musí splňovat tyto podmínky:
- Jméno funkce začíná slovem „Test“.
- Jediný parametr funkce má typ *testing.T, tedy ukazatel na strukturu T definovanou v balíčku testing.
- Funkce nemá návratovou hodnotu.
Prázdný test, jenž ve skutečnosti nebude provádět žádnou kontrolu podmínek, ovšem bude obsahovat popis, jaká operace/funkce/metoda se testuje, může vypadat následovně:
package main import ( . "github.com/franela/goblin" "testing" ) func Test(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { }) }
Povšimněte si, že v samotném testu je nutné inicializovat kontext spravovaný knihovnou Goblin:
g := Goblin(t)
Následně je již možné vytvořenou strukturu použít pro volání dalších metod, zde konkrétně metody Describe, které se předá jak popis testované oblasti, tak i (anonymní) funkce s vlastními kontrolami (zde je funkce prozatím prázdná, protože se jedná pouze o kostru testu):
g.Describe("Adder", func() { // prázdné tělo anonymní funkce })
Tento test spustíme standardním způsobem, tedy jako jakýkoli jiný jednotkový test, příkazem go test. Přitom je vhodné použít i přepínač -v, aby se vypsaly informace i o těch krocích, které nezhavarovaly:
$ go test -v 01_intro_test.go === RUN Test --- PASS: Test (0.00s) PASS ok command-line-arguments 0.001s
Předchozí demonstrační příklad obsahoval pouze popis testované podmínky, ale ne již vlastní implementaci testu. Tento nedostatek ihned napravíme, protože následující test již kontroluje operaci součtu dvou kladných čísel. Povšimněte si zejména způsobů použití metod It a Assert (zde spojené s metodou Equal):
package main import ( . "github.com/franela/goblin" "testing" ) func Test(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { g.It("Should add two numbers ", func() { g.Assert(1 + 1).Equal(2) }) }) }
Tento test opět spustíme standardním způsobem, tedy s využitím příkazu go test. Výsledek po spuštění již bude vypadat odlišně, protože vlastně uvidíme dva výpisy – první výpis (strukturovaný) je proveden knihovnou Goblin, druhý (standardní PASS/FAIL) pak knihovnou Testing, resp. přesněji řečeno nástroji, které výstup z knihovny Testing dále zpracovávají a zobrazují uživateli na terminálu:
$ go test -v 02_passing_test.go === RUN Test Adder ✓ Should add two numbers 1 tests complete (0 ms) --- PASS: Test (0.00s) PASS ok command-line-arguments 0.001s
Pochopitelně můžeme testy spustit i bez přepínače -v; v takovém případě je ovšem výstup do značné míry zminimalizován:
$ go test 02_passing_test.go ok command-line-arguments 0.001s
3. Testy se složitější strukturou
Další možnosti knihovny Goblin si ověříme na příkladu, v němž se provádí čtyři kroky (testování podmínek). První podmínka je splněna (1+1=2), druhá nesplněna (1+1≠5), třetí podmínka není vůbec implementována a čtvrtá je explicitně vyloučena a nebude při testování vůbec spuštěna. V tomto případě se použije metoda Xit a nikoli metoda It:
package main import ( . "github.com/franela/goblin" "testing" ) func Test(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { g.It("Should add two numbers ", func() { g.Assert(1 + 1).Equal(2) }) g.It("Should add two numbers", func() { g.Assert(1 + 1).Equal(5) }) g.It("Should substract two numbers") g.Xit("Should add two numbers, excluded ", func() { g.Assert(3 + 1).Equal(4) }) }) }
Opět se podívejme na to, jakým způsobem bude vypadat výstup po spuštění testů:
$ go test -v 03_intro_test.go === RUN Test Adder ✓ Should add two numbers 1) Should add two numbers - Should substract two numbers - Should add two numbers, excluded 1 tests complete (0 ms) 1 test(s) pending 1 test(s) excluded 1 tests failed: 1) Adder Should add two numbers: 2 does not equal 5 /home/tester/go/src/github.com/franela/goblin/assertions.go:48 +0x130 /home/tester/src/go-root/article_42/03_intro_test.go:15 +0x68 /home/tester/go/src/github.com/franela/goblin/goblin.go:229 +0x27 /home/tester/go/src/github.com/franela/goblin/goblin.go:229 +0x3e7 --- FAIL: Test (0.00s) FAIL FAIL command-line-arguments 0.001s FAIL
Povšimněte si, že se správně ohlásily všechny čtyři situace, které po spuštění testů nastaly, knihovna Goblin totiž zaznamená i ty testy, které prozatím nejsou implementovány (to je důležitá zpráva pro vývojáře), i testy, které byly explicitně zakázány (opět důležitá zpráva).
Obrázek 3: Výsledek spuštění testů na terminálu s podporou barevného výstupu (což je vlastnost podporovaná všemi moderními terminály).
Ve skutečnosti se ovšem velmi často setkáme s testy, které mají složitější strukturu. V bloku Describe je například možné mít umístěno větší množství bloků It, pokaždé s odlišnou sadou podmínek. Takto strukturovaný test může vypadat například následovně:
package main import ( . "github.com/franela/goblin" "testing" ) func Test(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { g.It("Should add two numbers ", func() { g.Assert(1 + 1).Equal(2) }) g.It("Should add two numbers", func() { g.Assert(2 + 2).Equal(4) }) g.It("Should add two numbers", func() { g.Assert(10 + 20).Equal(30) }) }) }
Všechny testované podmínky se v tomto případě vypíšou odsazeně v sekci s nadpisem adder Adder:
$ go test -v 04_all_passing_test.go === RUN Test Adder ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers 3 tests complete (0 ms) --- PASS: Test (0.00s) PASS ok command-line-arguments 0.001s
Samozřejmě nám nic nebrání změnit popisky podsekcí It:
package main import ( . "github.com/franela/goblin" "testing" ) func Test(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { g.It("Should add 1+1 ", func() { g.Assert(1 + 1).Equal(2) }) g.It("Should add 2+2 ", func() { g.Assert(2 + 2).Equal(4) }) g.It("Should add 10+20", func() { g.Assert(10 + 20).Equal(30) }) }) }
Výsledek nyní bude pro uživatele čitelnější:
=== RUN Test Adder ✓ Should add 1+1 ✓ Should add 2+2 ✓ Should add 10+20 3 tests complete (0 ms) --- PASS: Test (0.00s) PASS ok command-line-arguments 0.001s
4. Vnořené bloky Describe
Boky Describe představované voláním metody G.Describe(), je možné v případě potřeby vnořovat do prakticky libovolné úrovně. Jeden testovací scénář tedy můžeme rozdělit na testování součtu dvou kladných čísel a na testování součtu kladného a záporného čísla:
package main import ( . "github.com/franela/goblin" "testing" ) func Test(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { g.Describe("Positive numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + 1).Equal(2) }) g.It("Should add two numbers", func() { g.Assert(2 + 2).Equal(4) }) g.It("Should add two numbers", func() { g.Assert(10 + 20).Equal(30) }) }) g.Describe("Negative numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + -1).Equal(0) }) g.It("Should add two numbers", func() { g.Assert(2 + -4).Equal(-2) }) g.It("Should add two numbers", func() { g.Assert(10 + -20).Equal(-10) }) }) }) }
Výsledek běhu testů bude nyní naformátován tímto způsobem:
$ go test -v 05_nested_describe_test.go === RUN Test Adder Positive numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers Negative numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers 6 tests complete (0 ms) --- PASS: Test (0.00s) PASS ok command-line-arguments 0.001s
Podobně lze pochopitelně bloky Describe vkládat za sebe. V dalším testu budeme nejdříve testovat chování operátoru + a následně i chování operátoru *:
package main import ( . "github.com/franela/goblin" "testing" ) func Test(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { g.Describe("Positive numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + 1).Equal(2) }) g.It("Should add two numbers", func() { g.Assert(2 + 2).Equal(4) }) g.It("Should add two numbers", func() { g.Assert(10 + 20).Equal(30) }) }) g.Describe("Negative numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + -1).Equal(0) }) g.It("Should add two numbers", func() { g.Assert(2 + -4).Equal(-2) }) g.It("Should add two numbers", func() { g.Assert(10 + -20).Equal(-10) }) }) }) g.Describe("Multiplier", func() { g.Describe("Positive numbers", func() { g.It("Should multiply two numbers ", func() { g.Assert(1 * 1).Equal(1) }) g.It("Should multiply two numbers", func() { g.Assert(2 * 2).Equal(4) }) g.It("Should add two numbers", func() { g.Assert(10 * 20).Equal(200) }) }) g.Describe("Negative numbers", func() { g.It("Should multiply two numbers ", func() { g.Assert(1 * -1).Equal(-1) }) g.It("Should multiply two numbers", func() { g.Assert(2 * -4).Equal(-8) }) g.It("Should multiply two numbers", func() { g.Assert(10 * -20).Equal(-200) }) }) }) }
Opět si samozřejmě ukážeme výsledky takto strukturovaného testovacího scénáře:
$ go test -v 06_nested_struct_test.go === RUN Test Adder Positive numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers Negative numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers 6 tests complete (0 ms) Multiplier Positive numbers ✓ Should multiply two numbers ✓ Should multiply two numbers ✓ Should multiply two numbers Negative numbers ✓ Should multiply two numbers ✓ Should multiply two numbers ✓ Should multiply two numbers 12 tests complete (0 ms) --- PASS: Test (0.00s) PASS ok command-line-arguments 0.001s
5. Větší množství funkcí s implementací testů
Nic nám pochopitelně nebrání v deklaraci většího množství funkcí s implementací testů. V následujícím demonstračním příkladu jsou použity dvě funkce pojmenované jednoduše TestAdder (test chování operátoru +) a TestMultiplier (test chování operátoru *):
package main import ( . "github.com/franela/goblin" "testing" ) func TestAdder(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { g.Describe("Positive numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + 1).Equal(2) }) g.It("Should add two numbers", func() { g.Assert(2 + 2).Equal(4) }) g.It("Should add two numbers", func() { g.Assert(10 + 20).Equal(30) }) }) g.Describe("Negative numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + -1).Equal(0) }) g.It("Should add two numbers", func() { g.Assert(2 + -4).Equal(-2) }) g.It("Should add two numbers", func() { g.Assert(10 + -20).Equal(-10) }) }) }) } func TestMultiplier(t *testing.T) { g := Goblin(t) g.Describe("Multiplier", func() { g.Describe("Positive numbers", func() { g.It("Should multiply two numbers ", func() { g.Assert(1 * 1).Equal(1) }) g.It("Should multiply two numbers", func() { g.Assert(2 * 2).Equal(4) }) g.It("Should multiply two numbers", func() { g.Assert(10 * 20).Equal(200) }) }) g.Describe("Negative numbers", func() { g.It("Should multiply two numbers ", func() { g.Assert(1 * -1).Equal(-1) }) g.It("Should multiply two numbers", func() { g.Assert(2 * -4).Equal(-8) }) g.It("Should multiply two numbers", func() { g.Assert(10 * -20).Equal(-200) }) }) }) }
Výsledky nyní budou rozděleny na dvě části, přičemž se každá část bude vztahovat k jedné funkci, v níž jsou jednotkové testy implementovány:
$ go test -v 07_split_test.go === RUN TestAdder Adder Positive numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers Negative numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers 6 tests complete (0 ms) --- PASS: TestAdder (0.00s) === RUN TestMultiplier Multiplier Positive numbers ✓ Should multiply two numbers ✓ Should multiply two numbers ✓ Should multiply two numbers Negative numbers ✓ Should multiply two numbers ✓ Should multiply two numbers ✓ Should multiply two numbers 6 tests complete (0 ms) --- PASS: TestMultiplier (0.00s) PASS ok command-line-arguments 0.001s
Další ukázka, tentokrát pro podmínky, které nejsou splněny:
package main import ( . "github.com/franela/goblin" "testing" ) func Test(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { g.Describe("Positive numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + 1).Equal(2) }) g.It("Should add two numbers", func() { g.Assert(2 + 2).Equal(4) }) g.It("Should add two numbers", func() { g.Assert(10 + 20).Equal(-100) }) }) g.Describe("Negative numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + -1).Equal(0) }) g.It("Should add two numbers", func() { g.Assert(2 + -4).Equal(-2) }) g.It("Should add two numbers", func() { g.Assert(10 + -20).Equal(-10) }) }) }) g.Describe("Multiplier", func() { g.Describe("Positive numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 * 1).Equal(1) }) g.It("Should add two numbers", func() { g.Assert(2 * 2).Equal(4) }) g.It("Should add two numbers", func() { g.Assert(10 * 20).Equal(200) }) }) g.Describe("Negative numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 * -1).Equal(-1) }) g.It("Should add two numbers", func() { g.Assert(2 * -4).Equal(-8) }) g.It("Should add two numbers", func() { g.Assert(10 * -20).Equal(200) }) }) }) }
Výsledky:
=== RUN Test Adder Positive numbers ✓ Should add two numbers ✓ Should add two numbers 1) Should add two numbers Negative numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers 5 tests complete (0 ms) 1 tests failed: 1) Positive numbers Should add two numbers: 30 does not equal -100 /home/ptisnovs/go/src/github.com/franela/goblin/assertions.go:48 +0x130 /home/ptisnovs/src/go-root/article_42/08_nested_struct_failed_test.go:19 +0x68 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x27 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x3e7 Multiplier Positive numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers Negative numbers ✓ Should add two numbers ✓ Should add two numbers 2) Should add two numbers 10 tests complete (0 ms) 2 tests failed: 1) Positive numbers Should add two numbers: 30 does not equal -100 /home/ptisnovs/go/src/github.com/franela/goblin/assertions.go:48 +0x130 /home/ptisnovs/src/go-root/article_42/08_nested_struct_failed_test.go:19 +0x68 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x27 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x3e7 2) Negative numbers Should add two numbers: -200 does not equal 200 /home/ptisnovs/go/src/github.com/franela/goblin/assertions.go:48 +0x130 /home/ptisnovs/src/go-root/article_42/08_nested_struct_failed_test.go:54 +0x68 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x27 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x3e7 --- FAIL: Test (0.00s) FAIL FAIL command-line-arguments 0.001s FAIL
Podobný test, ovšem tentokrát strukturovaný do dvou samostatných funkcí:
package main import ( . "github.com/franela/goblin" "testing" ) func Test1(t *testing.T) { g := Goblin(t) g.Describe("Adder", func() { g.Describe("Positive numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + 1).Equal(2) }) g.It("Should add two numbers", func() { g.Assert(2 + 2).Equal(4) }) g.It("Should add two numbers", func() { g.Assert(10 + 20).Equal(-30) }) }) g.Describe("Negative numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 + -1).Equal(0) }) g.It("Should add two numbers", func() { g.Assert(2 + -4).Equal(-2) }) g.It("Should add two numbers", func() { g.Assert(10 + -20).Equal(-10) }) }) }) } func Test2(t *testing.T) { g := Goblin(t) g.Describe("Multiplier", func() { g.Describe("Positive numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 * 1).Equal(1) }) g.It("Should add two numbers", func() { g.Assert(2 * 2).Equal(4) }) g.It("Should add two numbers", func() { g.Assert(10 * 20).Equal(200) }) }) g.Describe("Negative numbers", func() { g.It("Should add two numbers ", func() { g.Assert(1 * -1).Equal(-1) }) g.It("Should add two numbers", func() { g.Assert(2 * -4).Equal(-8) }) g.It("Should add two numbers", func() { g.Assert(10 * -20).Equal(200) }) }) }) }
Výsledky:
=== RUN Test1 Adder Positive numbers ✓ Should add two numbers ✓ Should add two numbers 1) Should add two numbers Negative numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers 5 tests complete (0 ms) 1 tests failed: 1) Positive numbers Should add two numbers: 30 does not equal -30 /home/ptisnovs/go/src/github.com/franela/goblin/assertions.go:48 +0x130 /home/ptisnovs/src/go-root/article_42/09_split_failed_test.go:19 +0x68 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x27 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x3e7 --- FAIL: Test1 (0.00s) === RUN Test2 Multiplier Positive numbers ✓ Should add two numbers ✓ Should add two numbers ✓ Should add two numbers Negative numbers ✓ Should add two numbers ✓ Should add two numbers 1) Should add two numbers 5 tests complete (0 ms) 1 tests failed: 1) Negative numbers Should add two numbers: -200 does not equal 200 /home/ptisnovs/go/src/github.com/franela/goblin/assertions.go:48 +0x130 /home/ptisnovs/src/go-root/article_42/09_split_failed_test.go:58 +0x68 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x27 /home/ptisnovs/go/src/github.com/franela/goblin/goblin.go:229 +0x3e7 --- FAIL: Test2 (0.00s) FAIL FAIL command-line-arguments 0.001s FAIL
6. Klauzule Before
V testovacích scénářích lze použít libovolné množství klauzulí Before představovaných (anonymními) funkcemi, které jsou zavolány ještě před kontrolou jednotlivých podmínek v testech:
package main import ( . "github.com/franela/goblin" "testing" ) func TestBefore(t *testing.T) { g := Goblin(t) x := 0 g.Describe("Adder", func() { g.It("x+1", func() { g.Assert(x + 1).Equal(1) }) }) g.Describe("Adder", func() { g.Before(func() { x = 10 }) g.It("x+1", func() { g.Assert(x + 1).Equal(11) }) }) g.Describe("Adder", func() { g.Before(func() { x = 1 }) g.It("x+1", func() { g.Assert(x + 1).Equal(2) }) }) }
V bloku Before se modifikuje hodnota proměnné x použité v testech:
=== RUN TestBefore Adder ✓ x+1 1 tests complete (0 ms) Adder ✓ x+1 2 tests complete (0 ms) Adder ✓ x+1 3 tests complete (0 ms) --- PASS: TestBefore (0.00s) PASS ok command-line-arguments 0.001s
Naprosto stejného výsledku lze dosáhnout, i když bude blok Before uveden až za It:
package main import ( . "github.com/franela/goblin" "testing" ) func TestBefore(t *testing.T) { g := Goblin(t) x := 0 g.Describe("Adder", func() { g.It("x+1", func() { g.Assert(x + 1).Equal(1) }) }) g.Describe("Adder", func() { g.It("x+1", func() { g.Assert(x + 1).Equal(11) }) g.Before(func() { x = 10 }) }) g.Describe("Adder", func() { g.It("x+1", func() { g.Assert(x + 1).Equal(2) }) g.Before(func() { x = 1 }) }) }
7. Testování aplikací s REST API s využitím knihovny Frisby
Ve druhé části dnešního článku se budeme zabývat popisem další knihovny určené pro zjednodušení testování aplikací. Tentokrát se bude jednat o knihovnu zaměřenou primárně na testování REST API, což je v praxi velmi užitečné, protože mnoho aplikací vytvořených v jazyce Go má podobu služeb a mikroslužeb právě s rozhraním REST API. Knihovna Frisby, kterou opět nalezneme na GitHubu, konkrétně na adrese github.com/verdverm/frisby, dokáže pracovat se všemi šesti základními HTTP metodami GET, POST, PUT, DELETE, PATCH, HEAD i s metodou OPTIONS. Podporována je i práce s hlavičkami dotazů (request) i odpovědí (response), zpracování cookies, předávání dat, testování obsahu odpovědi (jak textové, tak i v podobě JSONu) a dokonce je možné i specifikovat funkce, které mají zpracovat obsah odpovědi.
Obrázek 4: Logo knihovny Frisby.
Dnes si ukážeme základy práce s touto knihovnou, tj. se způsobem posílání dotazů na zvolené REST API endpointy, kontrolu odpovědí (včetně hlaviček) a taktéž s testováním vrácených dat, která jsou typicky předána ve formátu JSON. Ovšem možnosti knihovny Frisby jsou ve skutečnosti mnohem větší a s některými z nich se seznámíme v dalším pokračování seriálu o jazyku Go.
8. Otestování koncových bodů REST API přístupných přes HTTP metodu GET
S možnostmi nabízenými knihovnou Frisby se seznámíme s využitím několika demonstračních příkladů. První příklad bude velmi jednoduchý – budeme v něm testovat HTTP kódy vrácené pro adresy http://httpbin.org/get a http://httpbin.org/status/321 při použití HTTP metody GET. V prvním případě by se měl vrátit kód 200 (OK), ve druhém pak kód 321. Samotné vytvoření dotazu proběhne tímto způsobem:
f := frisby.Create("Simplest test") f..Get("http://httpbin.org/get")
Alternativně je možné přípravu dotazu zapsat na jediný řádek:
f := frisby.Create("Simplest test").Get("http://httpbin.org/get")
Tento řádek pouze připraví objekt reprezentující dotaz. Samotný dotaz (HTTP GET) bude proveden až spuštěním dalšího řádku:
f.Send()
Na řádku dalším pak probíhá test na stavový kód HTTP vrácený serverem v odpovědi:
f.ExpectStatus(200)
Na samotném konci testu si necháme vypsat výsledky:
frisby.Global.PrintReport()
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Simplest test").Get("http://httpbin.org/get") f.Send() f.ExpectStatus(200) f = frisby.Create("Check HTTP code").Get("http://httpbin.org/status/321") f.Send() f.ExpectStatus(321) frisby.Global.PrintReport() }
Tento test spustíme běžným příkazem go run, protože se nejedná o skutečný jednotkový test:
$ go run 10_frisby_basic_usage.go For 2 requests made All tests passed
Použité funkce a metody:
func Create(name string) *Frisby func (F *Frisby) Get(url string) *Frisby func (F *Frisby) Send() *Frisby func (F *Frisby) ExpectStatus(code int) *Frisby
9. Chování Frisby ve chvíli, kdy není splněna testovaná podmínka
V případě, že nějaká testovaná podmínka není splněna, nebude výstup testů tak minimalistický, jako tomu bylo v předchozím příkladu. Můžeme se o tom ostatně velmi snadno přesvědčit, protože v následujícím příkladu jsou testovány tři koncové body REST API, přitom dvě podmínky (na HTTP kódy) nebudou po spuštění testů splněny:
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Simplest test").Get("http://httpbin.org/get") f.Send() f.ExpectStatus(200) f = frisby.Create("Test that fails").Get("http://httpbin.org/get") f.Send() f.ExpectStatus(404) f = frisby.Create("Another test that fails").Get("http://httpbin.org/status/456") f.Send() f.ExpectStatus(404) frisby.Global.PrintReport() }
Po spuštění získáme podrobnější informace o dvou chybách:
$ go run 11_frisby_failures.go For 3 requests made FAILED [2/3] [Test that fails] - Expected Status 404, but got 200: "200 OK" [Another test that fails] - Expected Status 404, but got 456: "456 UNKNOWN"
10. Kontrola hlaviček odpovědí poslaných serverem
Po přijetí odpovědi serveru můžeme zjišťovat i to, zda jsou správně nastaveny všechny požadované hlavičky. V dalším demonstračním příkladu zjišťujeme, jestli je nastavena hlavička se jménem „Server“. Předpokládáme, že tato hlavička bude obsahovat hodnotu „nginx“:
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Headers check").Get("http://httpbin.org/get") f.Send() f.ExpectStatus(200) f.ExpectHeader("Server", "nginx") frisby.Global.PrintReport() }
Spuštění tohoto testu nám prozradí, že podmínka byla splněna – odpověď nám skutečně poslal server nginx popř. jiný server, který se jen jako nginx maskuje:
$ go run 12_frisby_check_headers.go For 1 requests made All tests passed
Pokud naopak budeme očekávat odlišný server, například „apache“, měl by test zhavarovat:
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Headers check").Get("http://httpbin.org/get") f.Send() f.ExpectStatus(200) f.ExpectHeader("Server", "apache") frisby.Global.PrintReport() }
Můžeme se o tom velmi snadno přesvědčit spuštěním druhé varianty testu:
$ go run 12B_frisby_check_headers.go For 1 requests made FAILED [1/2] [Headers check] - Expected Header "Server" to be "apache", but got "nginx"
V případě, že budete chtít zobrazit výsledky jakéhokoli kroku testu, lze (prakticky kdykoli) zavolat metodu PrintReport (jedná se o metodu objektu Frisby):
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Simplest test") f.Get("http://httpbin.org/get") f.Send() f.ExpectStatus(200) f.PrintReport() f = frisby.Create("Check HTTP code").Get("http://httpbin.org/status/321") f.Send() f.ExpectStatus(321) f.PrintReport() frisby.Global.PrintReport() }
Výsledek bude do značné míry podobný výsledkům spuštění jednotkových testů.
11. Použití HTTP metody POST
Použití HTTP metody POST namísto metody GET je snadné, zejména ve chvíli, kdy serveru neposíláme v dotazu žádná data:
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Simplest test").Post("http://httpbin.org/post") f.Send() f.ExpectStatus(200) frisby.Global.PrintReport() }
Test v tomto případě nezhavaruje, protože koncový bod http://httpbin.org/post skutečně očekává použití metody POST a nikoli GET:
$ go run 13_frisby_post.go For 1 requests made All tests passed
O tom, že se má použít metoda POST a nikoli GET se lze snadno přesvědčit nepatrnou úpravou testu:
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Simplest test").Get("http://httpbin.org/post") f.Send() f.ExpectStatus(200) frisby.Global.PrintReport() }
S výsledkem:
$ go run 13B_frisby_post.go For 1 requests made FAILED [1/1] [Simplest test] - Expected Status 200, but got 405: "405 METHOD NOT ALLOWED"
12. Poslání dat ve formátu JSON na server
Mnoho služeb s rozhraním REST API akceptuje (a také posílá) data ve formátu JSON, což znamená, že musíme být schopni s takovými daty pracovat i v nástroji Frisby. Nejdříve je většinou nutné správně nastavit hlavičky HTTP dotazu – určit, že se přenáší data ve formátu JSON a že akceptujeme data (v odpovědi) ve stejném formátu:
... ... ... SetHeader("Content-Type", "application/json"). SetHeader("Accept", "application/json"). ... ... ...
Pokud mají přenášená data jednoduchý formát, například se jedná o pole či o mapy, může být poslání dat snadné. V následujícím úryvku kódu se přenese JSON obsahující pole se třemi prvky typu řetězec (konverze se provádí automaticky a interně):
... ... ... SetJson([]string{"item1", "item2", "item3"}) ... ... ...
Samozřejmě je možné přenést i složitější data, například již zmíněnou mapu:
data := map[string]string{ "text": "Hello **world**!", } f = frisby.Create("Markdown conversion").Post("https://api.github.com/markdown").SetJson(data)
Podívejme se nyní na úplný zdrojový kód příkladu, který nejdříve přenese data ve formě pole a poté ve formě mapy (ve druhém případě se na straně služby provede konverze přeneseného textu z Markdownu do HTML):
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Simplest test").Post("http://httpbin.org/post"). SetHeader("Content-Type", "application/json").SetHeader("Accept", "application/json"). SetJson([]string{"item1", "item2", "item3"}) f.Send() f.ExpectStatus(200) data := map[string]string{ "text": "Hello **world**!", } f = frisby.Create("Markdown conversion").Post("https://api.github.com/markdown").SetJson(data) f.Send() f.ExpectStatus(200) frisby.Global.PrintReport() }
Překlad a spuštění tohoto příkladu by nemělo vést k žádné chybě:
$ go run 14_frisby_post_json.go For 2 requests made All tests passed
13. Kontrola obsahu odpovědi serveru
Kontrolovat samozřejmě můžeme i data poslaná v odpovědi serveru. Nejjednodušší je v tomto případě použití metody ExpectContent(), která se snaží v těle odpovědi nalézt předaný řetězec (ať již se nachází kdekoli):
... ... ... f.ExpectHeader("Content-Type", "text/html; charset=utf-8"). ExpectContent("The Go Programming Language") ... ... ...
Samozřejmě, že je možné hledat i části odpovědi zapsané v HTML. Víme již, že služba na adrese api.github.com/markdown dokáže převést předaný text z Markdownu do HTML, takže můžeme psát:
data := map[string]string{ "text": "Hello **world**!", } f = frisby.Create("Markdown conversion").Post("https://api.github.com/markdown").SetJson(data) f.Send() f.ExpectContent("<p>Hello <strong>world</strong>!</p>")
Dokonce je možné si nechat obsah těla odpovědi vytisknout, a to metodou PrintBody:.
Úplný kód dalšího demonstračního příkladu vypadá následovně:
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Content check").Get("http://golang.org") f.Send() f.ExpectStatus(200) f.ExpectHeader("Content-Type", "text/html; charset=utf-8").ExpectContent("The Go Programming Language") data := map[string]string{ "text": "Hello **world**!", } f = frisby.Create("Markdown conversion").Post("https://api.github.com/markdown").SetJson(data) f.Send() f.ExpectStatus(200) f.ExpectContent("<p>Hello <strong>world</strong>!</p>") f.PrintBody() frisby.Global.PrintReport() }
Po spuštění tohoto příkladu by se mělo zobrazit hlášení převedené z Markdownu do HTML:
$ go run 15_frisby_check_content.go <p>Hello <strong>world</strong>!</p> For 2 requests made All tests passed
14. Nastavení cookies
V mnoha aplikacích je navíc nutné pracovat i s cookies. Nastavení cookie se provádí metodou SetCookie, které se předává jméno cookie i její hodnota (pozor na to, že v dokumentaci je napsáno SetCookies, což je jiná metoda určená po nastavení více cookies zavoláním jediné funkce). Nastavení cookies lze velmi snadno otestovat na adrese http://httpbin.org/cookies, kde běží služba vracející (ve formátu JSON) hodnoty všech nastavených cookies:
$ curl --cookie "foo=bar" http://httpbin.org/cookies { "cookies": { "foo": "bar" } }
Podobný test můžeme zapsat i v jazyce Go s využitím knihovny Frisby:
package main import "github.com/verdverm/frisby" func main() { f := frisby.Create("Cookies check").Get("http://httpbin.org/cookies").SetCookie("foo", "bar") f.Send() f.ExpectStatus(200) f.ExpectJson("cookies.foo", "bar") f.PrintBody() frisby.Global.PrintReport() }
Výsledek, jenž získáme po spuštění tohoto příkladu:
$ go run 16_frisby_cookies.go { "cookies": { "foo": "bar" } } For 1 requests made All tests passed
15. Způsob zápisu testů
Nic nám nebrání používat různé zápisy testů. Jeden z možných přístupů spočívá v explicitním volání jednotlivých metod postupně za sebou tak, jak se celý test bude vykonávat:
f := frisby.Create("Cookies check") f.Get("http://httpbin.org/cookies") f.SetCookie("foo", "bar") f.Send() f.ExpectStatus(200) f.ExpectJson("cookies.foo", "bar") f.PrintBody()
Alternativně (díky použití fluent interface) se metody mohou zřetězit, a to do té míry, že vlastně ani není nutné explicitně vytvářet proměnnou pro hodnotu typu *Frisby:
package main import "github.com/verdverm/frisby" func main() { frisby.Create("Cookies check"). Get("http://httpbin.org/cookies"). SetCookie("foo", "bar"). Send(). ExpectStatus(200). ExpectJson("cookies.foo", "bar"). PrintBody() frisby.Global.PrintReport() }
Tento demonstrační příklad se bude chovat podobně, jako příklad z předchozí kapitoly:
$ go run 17_frisby_threading.go { "cookies": { "foo": "bar" } } For 1 requests made All tests passed
16. Využití knihovny Frisby společně s knihovnou Testing
Knihovnu Frisby můžeme využít společně s knihovnou Testing v jednotkových testech. V takovém případě využijeme metodu Error, která vrací buď hodnotu nil nebo strukturu popisující chybu, k níž došlo. Jak se s takovou chybou v jednotkových testech nakládá již víme – musíme zavolat metodu Testing.Error:
package main import ( "github.com/verdverm/frisby" "testing" ) func TestHttpGet(t *testing.T) { f := frisby.Create("Simplest test").Get("http://httpbin.org/get") f.Send() f.ExpectStatus(200) err := f.Error() if err != nil { t.Error(err) } }
Nyní musíme celý test spustit nikoli pomocí go run, ale s využitím go test:
$ go test -v 18_frisby_as_test_pass_test.go === RUN TestHttpGet --- PASS: TestHttpGet (0.21s) PASS ok command-line-arguments 0.217s
17. Padající jednotkový test založený na knihovně Frisby
Samozřejmě si můžeme otestovat, co se stane ve chvíli, kdy podmínka, kterou testujeme s využitím knihovny Frisby, není splněna. V dalším příkladu očekáváme, že se vrátí HTTP kód 400, ovšem ve skutečnosti se vrátí 200 OK:
package main import ( "github.com/verdverm/frisby" "testing" ) func TestHttpGet(t *testing.T) { f := frisby.Create("Simplest test").Get("http://httpbin.org/get") f.Send() f.ExpectStatus(400) err := f.Error() if err != nil { t.Error(err) } }
Povšimněte si, že popis chyby přesně odpovídá testované podmínce a napoví testerovi, kde chyba nastala a z jaké příčiny (alespoň z pohledu vlastních testů):
$ go test -v 19_frisby_as_test_fail_test.go === RUN TestHttpGet400Ok --- FAIL: TestHttpGet400Ok (0.21s) 20_frisby_more_test.go:24: Expected Status 400, but got 200: "200 OK" FAIL FAIL command-line-arguments 0.427s FAIL
18. Jednotkové testy s více funkcemi
V posledním demonstračním příkladu, který si dnes ukážeme, jsou v jednotkovém testu použity dvě funkce, takže se změní i vygenerovaný výsledek:
package main import ( "github.com/verdverm/frisby" "testing" ) func TestHttpGet200Ok(t *testing.T) { f := frisby.Create("Simplest test for HTTP 200 OK").Get("http://httpbin.org/get") f.Send() f.ExpectStatus(200) err := f.Error() if err != nil { t.Error(err) } } func TestHttpGet400Ok(t *testing.T) { f := frisby.Create("Simplest test for HTTP 400 Bad Request").Get("http://httpbin.org/get") f.Send() f.ExpectStatus(400) err := f.Error() if err != nil { t.Error(err) } }
V tomto případě by měl první test proběhnout v pořádku a druhý by měl zahlásit chybu:
$ go test -v 20_frisby_more_test.go === RUN TestHttpGet200Ok --- PASS: TestHttpGet200Ok (0.21s) === RUN TestHttpGet400Ok --- FAIL: TestHttpGet400Ok (0.21s) 20_frisby_more_test.go:24: Expected Status 400, but got 200: "200 OK" FAIL FAIL command-line-arguments 0.427s FAIL
frisby.Global.PrintReport()
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 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 megabajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- 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