Hlavní navigace

Testování aplikací psaných v Go s využitím knihoven Goblin a Frisby

12. 11. 2019
Doba čtení: 49 minut

Sdílet

Dnes si popíšeme knihovny Goblin a Frisby. První knihovna zjednodušuje zápis jednotkových či integračních testů, druhá pak slouží pro testování REST API, což je přesně oblast, ve které se jazyk Go velmi často používá.

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

4. Vnořené bloky Describe

5. Větší množství funkcí s implementací testů

6. Klauzule Before

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

11. Použití HTTP metody POST

12. Poslání dat ve formátu JSON na server

13. Kontrola obsahu odpovědi serveru

14. Nastavení cookies

15. Způsob zápisu testů

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

20. Odkazy na Internetu

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.

Poznámka: v případě, že se ve vlastním testu nemusí použít kontext (spravovaný knihovnou Mocha), lze zápis zkrátit následujícím způsobem:
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.

Poznámka: propojení Testing+Goblin představuje dobrý a praktický přístup, protože na knihovnu testing jsou navázány další nástroje používané v ekosystému programovacího jazyka Go. Díky tomu, že Goblin rozšiřuje a doplňuje možnosti testing (a nenahrazuje je), můžeme stále používat go test, knihovnu Gocov atd.

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:

  1. Jméno funkce začíná slovem „Test“.
  2. Jediný parametr funkce má typ *testing.T, tedy ukazatel na strukturu T definovanou v balíčku testing.
  3. 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)
                })
        })
}
Poznámka: podmínku můžete v případě potřeby zapsat i explicitně pomocí if podmínka, většinou to ale není nutné.

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).

Poznámka: ve skutečnosti je výstup získaný spuštěním jednotkových testů obarven, takže na terminálu může vypadat takto:

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
Poznámka: povšimněte si, že je zde použit pattern builder umožňující zřetězení jednotlivých metod do jediného příkazu (zobecněním tohoto konceptu vzniká fluent interface, což je termín, který zavedl známý Martin Fowler).

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"
Poznámka: povšimněte si, že výsledek testů je poměrně dobře čitelný, protože se zobrazí jak očekávaná hodnota, tak i hodnota, kterou jsme skutečně získali (a to bez nutnosti explicitního nastavení těchto informací).

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()
}
Poznámka: povšimněte si, že je možné otestovat, zda vrácený JSON obsahuje určitou hodnotu. Pro tento účel se používá metoda nazvaná ExpectJson, v jejímž prvním parametru se specifikuje selektor a ve druhém pak očekávaná hodnota. V tomto konkrétním případě selektor vybírá z mapy (představované formátem JSON) nejdříve hodnotu pod klíčem cookies a posléze pod klíčem foo.

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:

root_podpora

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
Poznámka: můžete zkombinovat i oba způsoby, tj. použití jednotkových testů (se všemi z toho plynoucími výhodami) a zavolání:
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:

# Příklad Stručný popis Cesta
1 01_intro_test.go kostra testu vytvořeného pomocí knihovny Goblin https://github.com/tisnik/go-root/blob/master/article42/01_in­tro_test.go
2 02_passing_test.go ukázka otestování operátoru + s využitím Assert a Equal https://github.com/tisnik/go-root/blob/master/article42/02_pas­sing_test.go
3 03_intro_test.go čtyři možné výsledky testů: passed, failed, not implemented, skipped https://github.com/tisnik/go-root/blob/master/article42/03_in­tro_test.go
4 04_all_passing_test.go složitější struktura testů https://github.com/tisnik/go-root/blob/master/article42/04_a­ll_passing_test.go
5 04B_all_passing_test.go vylepšení zpráv z předchozího testu https://github.com/tisnik/go-root/blob/master/article42/04B_a­ll_passing_test.go
6 05_nested_describe_test.go složitější struktura testů https://github.com/tisnik/go-root/blob/master/article42/05_nes­ted_describe_test.go
7 06_nested_struct_test.go složitější struktura testů https://github.com/tisnik/go-root/blob/master/article42/06_nes­ted_struct_test.go
8 07_split_test.go rozdělení jednotkových testů do dvou funkcí https://github.com/tisnik/go-root/blob/master/article42/07_split_tes­t.go
9 08_nested_struct_failed_test.go složitější struktura testů, které havarují https://github.com/tisnik/go-root/blob/master/article42/08_nes­ted_struct_failed_test.go
10 09_split_failed_test.go složitější struktura testů, které havarují https://github.com/tisnik/go-root/blob/master/article42/09_split_fa­iled_test.go
       
11 10_frisby_basic_usage.go otestování koncových bodů REST API přístupných přes HTTP metodu GET https://github.com/tisnik/go-root/blob/master/article42/10_fris­by_basic_usage.go
12 10B_frisby_basic_usage.go otestování koncových bodů REST API přístupných přes HTTP metodu GET https://github.com/tisnik/go-root/blob/master/article42/10B_fris­by_basic_usage.go
13 10C_frisby_basic_usage.go tisk výsledku každého kroku testu https://github.com/tisnik/go-root/blob/master/article42/10C_fris­by_basic_usage.go
14 11_frisby_failures.go chování Frisby ve chvíli, kdy není splněna testovaná podmínka https://github.com/tisnik/go-root/blob/master/article42/11_fris­by_failures.go
15 12_frisby_check_headers.go kontrola hlaviček odpovědí poslaných serverem https://github.com/tisnik/go-root/blob/master/article42/12_fris­by_check_headers.go
16 12B_frisby_check_headers.go kontrola hlaviček odpovědí poslaných serverem https://github.com/tisnik/go-root/blob/master/article42/12B_fris­by_check_headers.go
17 13_frisby_post.go použití HTTP metody POST https://github.com/tisnik/go-root/blob/master/article42/13_fris­by_post.go
18 13B_frisby_post.go použití HTTP metody GET ve chvíli, kdy se očekává POST https://github.com/tisnik/go-root/blob/master/article42/13B_fris­by_post.go
18 14_frisby_post_json.go poslání dat ve formátu JSON na server https://github.com/tisnik/go-root/blob/master/article42/14_fris­by_post_json.go
19 15_frisby_check_content.go kontrola obsahu odpovědi serveru https://github.com/tisnik/go-root/blob/master/article42/15_fris­by_check_content.go
20 16_frisby_cookies.go nastavení cookies https://github.com/tisnik/go-root/blob/master/article42/16_fris­by_cookies.go
21 17_frisby_threading.go zřetězení volání metod https://github.com/tisnik/go-root/blob/master/article42/17_fris­by_threading.go
22 18_frisby_as_test_pass_test.go Frisby v jednotkových testech https://github.com/tisnik/go-root/blob/master/article42/18_fris­by_as_test_pass_test.go
23 19_frisby_as_test_fail_test.go Frisby v jednotkových testech https://github.com/tisnik/go-root/blob/master/article42/19_fris­by_as_test_fail_test.go
24 20_frisby_more_test.go Frisby v jednotkových testech https://github.com/tisnik/go-root/blob/master/article42/20_fris­by_more_test.go

20. Odkazy na Internetu

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

Byl pro vás článek přínosný?