Go Home Page
Die Programmiersprache Go

Tutorial: Create a Go Module — Deutsche Übersetzung

Das Original:
https://golang.org/doc/tutorial/create-module
Version of February 12, 2021
Diese Übersetzung:
https://bitloeffel.de/DOC/golang/create-module_20210309_de.html
Stand: 09.03.2021
© 2021 Hans-Werner Heinzen @ Bitloeffel.de
Die Nutzung dieses Werks ist unter den Bedingungen der "Creative Commons Attribution 3.0"-Lizenz erlaubt.
Für Fachbegriffe und ähnliches gibt es hier noch eine Wörterliste.

Übung: Module erstellen

Im Verlauf dieser Übung werden Sie zwei Module erstellen. Erstens eine Bibliothek, die von anderen Bibliotheken oder Programmen importiert werden kann. Zweitens ein Programm, welches das erste benutzt.

Diese Übung behandelt sieben Themen (in sieben Lektionen), die jeweils andere Aspekte der Sprache beleuchten.

  1. Erstellen eines Moduls — Schreiben Sie ein kleines Modul mit Funktionen, die Sie von einem anderen Modul rufen können.
  2. Rufen aus einem anderen Modul heraus — Importieren und benutzen Sie Ihr neues Modul.
  3. Rückgabe und Verarbeiten eines Fehlers — Fügen Sie eine einfache Fehlerbehandlung hinzu.
  4. Rückgabe einer zufälligen Grußformel — Verarbeiten Sie Daten mit Slices (das sind Arrays dynamischer Größe in Go).
  5. Rückgabe von Grußformeln für mehrere Adressaten — Speichern Sie Schlüssel/Wert-Paare in einer Map.
  6. Erstellen eines Tests — Benutzen Sie Go's Standardverfahren für Komponententests.
  7. Fertigen und Installieren der Anwendung — Fertigen und Installieren Sie Ihren Kode lokal.

Hinweis: Weitere Übungen finden Sie hier.

Voraussetzungen

1. Erstellen eines Moduls, das andere nutzen können

Beginnen wir mit dem Erstellen eines Go-Moduls (de). Ein Modul versammelt ein oder mehrere zusammengehörige Pakete, die einen eigenständigen, nützlichen Funktionssatz bilden. Zum Beispiel könnten Sie ein Modul mit Paketen zur Finanzanalyse erstellen, so dass andere, die eine Finanzanwendung schreiben, Ihre Vorarbeit nutzen können.

Go-Kode wird in Paketen gesammelt, und Pakete in Modulen. Das Modul eines Pakets benennt den Kontext, den Go benötigt um den Kode umzuwandeln; das ist zum einen die Version von Go, für das das Modul geschrieben wurde, und zum anderen die Menge der anderen Module, die gebraucht werden.

Im Laufe der Zeit ergänzen und verbessern Sie die Funktionalität Ihres Moduls und veröffentlichen immer neue Versionen. Entwickler, deren Kode Funktionen Ihres Moduls aufruft, können vor einer Produktionsübergabe das aktualisierte Paket importieren und mit der neuen Version testen.

  1. In einer Kommandozeile begeben Sie sich in ihr Home-Verzeichnis.

    Unter Linux oder Mac mit:

    $ cd
    

    Unter Windows mit:

    cd %HOMEPATH%
    
  2. Erstellen Sie für die Go-Modulquellen einen Ordner namens greetings. Dorthin schreiben Sie Ihren Kode.

    Das tun Sie zum Beispiel aus Ihrem Home-Verzeichnis heraus mit den folgenden Kommandos:

    $ mkdir greetings
    $ cd greetings
    
  3. Initialisieren Sie Ihr Modul mit dem Kommando go mod init, um die Datei go.mod zu erzeugen.

    Rufen Sie go mod init und geben Sie für das Modul den Pfadnamen mit, wo Ihr Kode zu finden sein wird. In unserem Beispiel verwenden wir example.com/greetings als Modulpfad — im Produktionskode wäre das dann die URL, von der Ihr Modul kopiert werden kann (download).

    $ go mod init example.com/greetings
    go: creating new go.mod: module example.com/greetings
    

    Das Kommando go mod init erzeugt eine Datei go.mod, die Ihren Kode als nutzbares Modul erkennen lässt. Die gerade eben erzeugte Datei enthält nur den Namen Ihres Moduls und die Go-Version, mit der Ihr Kode funktioniert. Doch für jede Abhängigkeit, die Sie hinzufügen — also jedem Paket aus einem anderen Modul — wird die Datei go.mod das zugehörige Modul inklusive seiner Version festhalten. Damit bleiben Umwandlungen reproduzierbar, und Sie haben unmittelbare Kontrolle darüber, welche Modulversionen benutzt werden.

  4. Erzeugen Sie mit einem Texteditor eine Datei für Ihren Kode und nennen sie greetings.go.
  5. Fügen Sie dort den folgenden Kode ein, und sichern sie die Datei.
    package greetings
    
    import "fmt"
    
    // Hello gibt einen Gruß an die genannte Person zurück.
    func Hello(name string) string {
        // Rückgabe eines Grußtexts mit eingefügtem Namen.
        message := fmt.Sprintf("Hi %v. Wie geht's.", name)
        return message
    }
    

    Das ist der erste Kode für ihr Modul. Er gibt jedem Rufer, der danach fragt, einen Gruß zurück. In der nächsten Lektion werden wir dann Kode schreiben, der diese Funktion ruft.

    Im obigen Kode tun Sie folgendes:

    • Sie deklarieren ein Paket greetings, um darin verwandte Funktionen zu sammeln.
    • Sie implementieren eine Funktion Hello, die einen Gruß zurückgibt.

      Diese Funktion nimmt einen Parameter name vom Typ string entgegen und gibt einen string zurück. In Go kann eine Funktion, deren Name mit einem Großbuchstaben beginnt, von einer Funktion außerhalb des Pakets gerufen werden. Das ist in Go als exportierter Name bekannt.

    • Sie deklarieren eine Variable message die den Gruß aufnimmt.

      In Go ist der Operator := eine Abkürzung zum Deklarieren und Initialisieren einer Variablen in einer Zeile. (Go erkennt am Wert auf der rechten Seite den Typ der Variablen.) In Langversion hätten Sie auch schreiben können:

      var message string
      message = fmt.Sprintf("Hi %v. Wie geht's.", name)
      
    • Sie benutzen die Funktion Sprintf aus dem Paket fmt, um eine Grußnachricht zu erzeugen. Beim ersten Argument handelt es sich um einen Formatstring; Sprintf ersetzt dort den Platzhalter %v mit dem Wert des Parameters name. Damit ist der Grußtext vollständig.
    • Sie geben den formatierten Grußtext an den Rufer zurück.

Im nächsten Schritt werden Sie diese Funktion von einem anderen Modul aus rufen.

2. Rufen aus einem anderen Modul heraus

In dieser Lektion werden wir Kode schreiben, der die Hello-Funktion des gerade erstellten Moduls ruft. Unser Kode wird als Programm ausführbar sein und Kode des greetings-Moduls nutzen.

  1. Erstellen Sie für die Go-Modulquellen einen Ordner namens hello. Dorthin schreiben Sie Ihren Kode.

    Wenn Sie sich gerade im greetings-Ordner befinden, können Sie das z.B. mit folgenden Kommandos tun:

    $ cd ..
    $ mkdir hello
    $ cd hello
    
  2. Erzeugen Sie mit einem Texteditor im hello-Ordner eine Datei für Ihren Kode und nennen Sie sie hello.go.
  3. Schreiben Sie Kode, mit dem Sie unsere Hello-Funktion rufen und geben Sie den zurückgegebenen Wert aus.

    Dazu können Sie in hello.go den folgendes einfügen:

    package main
    
    import (
        "fmt"
    
        "example.com/greetings"
    )
    
    func main() {
        // Gruß besorgen und ausgeben.
        message := greetings.Hello("Rosa")
        fmt.Println(message)
    }
    

    Im obigen Kode tun Sie folgendes:

    • Sie deklarieren ein main-Paket. Als Programm ausführbarer Kode muss sich bei Go im main-Paket befinden.
    • Sie importieren zwei Pakete, example.com/greetings und fmt. Damit bekommen Sie Zugriff auf (exportierte) Funktionen dieser Pakete. Indem Sie example.com/greetings importieren — das Paket, das sie vorhin erstellt haben — bekommen Sie Zugriff auf die Funktion Hello. Außerdem importieren Sie fmt, mit seinen Funktionen zur Ein- und Ausgabe von Text (wie etwa Ausgeben von Text auf die Konsole).
    • Sie kommen an einen Grußformel, indem Sie die Hello-Funktion des Pakets greetings aufrufen.
  4. Erzeugen Sie für dieses hello-Paket ein neues Modul.

    Führen Sie dazu auf der Kommandozeile aus dem Ordner hello das Kommando go mod init mit einem Modulnamen aus (hier soll er nur "hello" lauten).

    $ go mod init hello
    go: creating new go.mod: module hello
    
  5. Bearbeiten Sie jetzt das hello-Modul, um unser noch unveröffentlichtes greetings-Modul benutzen zu können.

    Für den produktiven Betrieb werden Sie wohl Ihr Modul auf einem Server veröffentlichen — entweder intern in Ihrer Firma oder im Internet — und das Go-Kommando wird es dann von dort kopieren können. Vorläufig aber müssen Sie das Modul des rufenden Programms modifizieren, damit der greetings-Kode auch in Ihrem lokalen Dateisystem gefunden wird.

    Dazu ändern Sie die Datei go.mod des Moduls hello an einer Stelle.

    1. Öffen Sie im Ordner hello die Datei go.mod, ändern Sie wie folgt und sichern die Datei wieder.
      module hello
      
      go 1.14
      
      replace example.com/greetings => ../greetings
      

      Jetzt sagt die replace-Direktive dem Go-Kommando, es soll den Modulpfad, also die URL example.com/greetings mit dem von Ihnen angegebenen Pfad ersetzen (replace). Hier ist das der greetings-Ordner direkt nebem dem hello-Ordner.

    2. Im hello-Ordner starten Sie jetzt go build, damit Go das Modul lokalisiert und es als Abhängigkeit in der go.mod-Datei ergänzt.
      $ go build
      go: found example.com/greetings in example.com/greetings v0.0.0-00010101000000-000000000000
      
    3. In der go.mod-Datei können Sie jetzt die Änderung sehen, die mit go build vorgenommen wurde; das ist die require-Direktive.
      module hello
      
      go 1.14
      
      replace example.com/greetings => ../greetings
      
      require example.com/greetings v0.0.0-00010101000000-000000000000
      

      Zum Umwandeln des Moduls fand Go den Kode lokal im Ordner ../greetings, fügte eine require-Direktive hinzu, die besagt, dass hello von example.com/greetings abhängt. Diese Abhängigkeit haben Sie erzeugt, indem Sie in hello.go das greetings-Paket aus dem greetings-Modul importiert haben. Die replace-Direktive sagt Go, wo der Kode zu finden ist, weil der bis jetzt noch nicht veröffentlicht wurde.

      Für ein öffentliches Modul würde die go-mod-Datei die replace-Direktive nicht enthalten und nur mit der require-Direktive und der dort angegebenen Versionsnummer arbeiten.

      require example.com/greetings v1.1.0
      
  6. Um zu sehen, ob der Kode funktioniert starten Sie im hello-Ordner die ausführbare Datei hello; diese wurde mit go build erzeugt.

    • Unter Linux oder Mac mit:
      $ ./hello
      Hi Rosa. Wie geht's.
      
    • Unter Windows mit:
      $ hello.exe
      Hi Rosa. Wie geht's.
      

Glückwunsch! Sie haben gerade zwei funktionierende Module geschrieben. Im nächsten Schritt werden Sie eine Fehlerbearbeitung ergänzen.

3. Rückgabe und Verarbeiten eines Fehlers

Handhaben von Fehlern ist für zuverlässigen Kode unverzichtbar. In dieser Lektion ergänzen Sie Kode, um einen Fehler aus dem greetings-Modul zurückzugeben und diesen dann im rufenden Programm zu verarbeiten.

  1. Ergänzen Sie greetings/greetings.go um den weiter unten hervorgehobenen Kode.

    Es macht wenig Sinn, einen Gruß zurückzugeben, wenn nicht bekannt ist, wer gegrüßt werden soll. Geben Sie also einen Fehler an den Rufer zurück, wenn name leer ist. Kopieren Sie die entsprechenden Zeilen nach greetings.go und speichen die Datei.

    package greetings
    
    import (
        "errors"
        "fmt"
    )
    
    // Hello gibt einen Gruß an die genannte Person zurück.
    func Hello(name string) (string, error) {
        // Wenn kein Name, dann Fehler mit Fehlermeldung zurückgeben.
        if name == "" {
            return "", errors.New("Name leer")
        }
    
        // Wenn mit Name, dann
        // Rückgabe eines Grußtexts mit eingefügtem Namen.
        message := fmt.Sprintf("Hi %v. Wie geht's.", name)
        return message, nil
    }
    

    Im obigen Kode tun Sie folgendes:

    • Sie Ändern die Funktion so, dass sie zwei Werte zurückgibt: einen string und ein error. Ihr Rufer wird den zweiten Wert prüfen und daran sehen, ob ein Fehler aufgetreten ist. (Funktionen in Go können mehrere Werte zurückgeben (de).)
    • Sie importieren aus der Go-Standardbibliothek das Paket errors, um daraus die Funktion errors.New benutzen zu können.
    • Sie fügen eine if-Anweisung hinzu, um auf eine ungültige Anfrage hin zu prüfen (ein leerer String, wo ein Name sein sollte), und in diesem Fall einen Fehler zurückzugeben. Die Funktion errors.New liefert einen error, der Ihre Fehlernachricht enthält.
    • Für den Erfolgsfall ergänzen Sie nil (was heißen soll: kein Fehler) in der return-Anweisung. Daran kann der Rufer erkennen, dass die Funktion erfolgreich war.
  2. Verarbeiten Sie in der Datei hello/hello.go den error-Wert, der nun von der Funktion Hello zusätzlich zu dem bisherigen Wert zurückgegeben wird.

    Ergänzen Sie hello.go um den hier hervorgehobenen Kode:

    package main
    
    import (
        "fmt"
        "log"
    
        "example.com/greetings"
    )
    
    func main() {
        // Festlegen der Eigenschaften des Standard-Loggers:
        // ein Präfix für den Log-Eintrag und ein Schalter, der die
        // Ausgabe von Zeit, Quelldateiname und Zeilennummer unterbindet.
        log.SetPrefix("greetings: ")
        log.SetFlags(0)
    
        // Anfordern einer Grußnachricht
        message, err := greetings.Hello("")
        // Einen zurückgelieferter Fehler auf der Konsole ausgeben,
        // dann das Programm verlassen.
        if err != nil {
            log.Fatal(err)
        }
    
        // Wenn ohne Fehler, Ausgabe der Nachricht auf der Konsole.
        fmt.Println(message)
    }
    

    Im obigen Kode tun Sie folgendes:

    • Sie richten das log-Paket so ein, dass am Beginn der Protokollnachricht der Kommandoname ("greetings"), aber keine Zeitangabe und keine Quelldateiinfos ausgegeben werden.
    • Sie weisen beide Rückgabewerte von Hello Variablen zu, also auch den error-Wert.
    • Sie vergessen Rosas Namen und geben als Argument für Hello einen leeren Sring mit, um so Ihre Fehlerverarbeitung testen zu können.
    • Sie fragen nach einem Nicht-nil error-Wert. In diesem Fall ist weitermachen sinnlos.
    • Sie nutzen Funktionen des log-Pakets aus der Standardbibliothek, um Ihre Fehlerinformation auszugeben. Tritt ein Fehler auf, so rufen Sie dessen Funktion Fatal zur Ausgabe und zum Abbrechen des Programms.
  3. Um zu bestätigen, dass der Kode funktioniert, starten Sie im hello-Ordner die hello.go-Datei mit go run.

    Da Sie einen leeren Namen mitgeben, tritt jetzt ein Fehler auf.

    $ go run hello.go
    greetings: Name leer
    exit status 1
    

So einfach ist Fehlerbehandlung in Go: Geben Sie dem Rufer einen error als Wert zurück, damit er ihn prüfen kann. Im nächsten Schritt werden Sie ein Go-typisches Slice benutzen, um einen zufällig gewählten Gruß zurückzugeben.

4. Rückgabe einer zufälligen Grußformel

In dieser Lektion ändern Sie den Kode so, dass er anstatt immer mit der gleichen Grußformel zu antworten eine von mehreren vorgefertigten Grußformeln zurückgibt.

Zu diesem Zweck benutzen Sie ein Slice. Ein Slice ist einem Array ähnlich, nur dass seine Größe sich dynamisch ändert, wenn Sie Elemente hinzufügen oder entfernen. Es ist einer der nützlichsten Typen in Go. Sie werden nun ein kleines Slice mit drei Grußformeln einfügen, und lassen Ihren Kode eine von diesen zufällig wählen und zurückgeben.

  1. Ändern Sie greetings/greetings.go so, dass Ihr Kode wie dieser hier ausschaut:
    package greetings
    
    import (
        "errors"
        "fmt"
        "math/rand"
        "time"
    )
    
    // Hello gibt einen Gruß an die genannte Person zurück.
    func Hello(name string) (string, error) {
        // Wenn kein Name, dann Fehler mit Fehlermeldung zurückgeben.
        if name == "" {
            return "", errors.New("Name leer")
        }
    
        // Erzeugen eines Grußes in zufälliger ausgewählter Form.
        message := fmt.Sprintf(randomFormat(), name)
        return message, nil
    }
    
    // init versorgt Anfangswerte von Variablen,
    // die später in Funktionen benutzt werden.
    func init() {
        rand.Seed(time.Now().UnixNano())
    }
    
    // randomFormat gibt aus einer Menge von Grußformeln
    // eine zurück; sie wird zufällig ausgewählt.
    func randomFormat() string {
        // Ein Slice von Formatstrings mit Grußformel.
        formats := []string{
            "Hi %v. Wie geht's.",
            "Schön dich zu sehen, %v.",
            "%v! Habe die Ehre!",
        }
    
        // Zufällige Wahl einer Grußformel mithilfe eines
        // zufällig ermittelten Index für das Slice.
        return formats[rand.Intn(len(formats))]
    }
    

    In diesem Kode tun Sie folgendes:

    • Sie fügen eine Funktion randomFormat hinzu, die ein zufällig ausgewähltes Grußformat zurückgibt. Beachten Sie, dass randomFormat mit einem Kleinbuchstaben beginnt, dass es also nur von Kode im selben Paket erreichbar ist; anders gesagt, randomFormat wird nicht exportiert.
    • Dort deklarieren Sie formats als Slice mit drei Grußformaten. Wenn ein Slice deklariert wird, enthält die eckige Klammer keine Größenangabe, wie hier bei []string. Damit sagt man Go, dass sich das Array, das hinter dem Slice steckt, dynamisch ändern kann.
    • Sie benutzen das math/rand-Paket, um eine Zufallszahl zum Indexzugriff auf das Slice zu erhalten.
    • Sie ergänzen eine init-Funktion (de), um dort das rand-Paket mit der aktuellen Zeit zu "impfen" (seed). init-Funktionen führt Go automatisch bei Programmstart aus, nachdem die globalen Variablen initialisiert worden sind.
    • Um einen Formatstring zu erhalten, rufen Sie in Hello die randomFormat-Funktion, und benutzen dieses Format zusammen mit dem Wert von name zum Erzeugen des Grußes.
    • Geben Sie — wie bisher — den Gruß (oder einen Fehler) zurück.

    Das hello.go kann bleiben, wie es ist.

  2. Wechseln Sie auf der Kommandozeile in den hello-Ordner und starten dort hello.go mit go run, um zu bestätigen, dass Ihr Kode funktioniert. Starten Sie mehrmals und beachten Sie die wechselden Grußformeln.

    Ah, Moment noch! — Vergessen Sie nicht den Namen Rosa (oder einen anderen nach Belieben) als Argument für den Aufruf der Hello-Funktion in hello.go: greetings.Hello("Rosa")

    $ go build
    $ ./hello
    Schön dich zu sehen, Rosa.
    
    $ ./hello
    Hi Rosa. Wie geht's.
    
    $ ./hello
    Rosa! Habe die Ehre!
    

Das war eine Einführung in Go's Slices. Um aus diesem Datentyp noch mehr rauszuholen, wollen wir noch mit einem Slice mehrere Personen grüßen. Das geschieht im nächsten Schritt.

5. Rückgabe von Grußformeln für mehrere Adressaten

Als letzte Änderung an Ihrem Modulkode werden Sie die Fähigkeit hinzufügen, Grüße für mehrere Personen auf eine einzige Anfrage hin zurückzugeben. Anders formuliert verarbeiten wir eine Eingabe, die aus mehreren Werten besteht, zu einer Ausgabe, die ebenfalls mehrere Werte enthält.

Zu diesem Zweck müssen Sie eine Menge von Namen an eine Funktion übergeben, die für jeden dieser Namen einen Gruß zurückgeben kann. Eine Änderung des Parameters von Hello von einem Einzelwert zu einer Menge von Werten würde die Signatur dieser Funktion ändern. Wenn Sie also das Modul greetings schon veröffentlicht hätten und andere bereits in ihrem Kode Hello gerufen hätten, würden deren Programme unbrauchbar. In so einem Fall ist es besser, neuen Fähigkeiten auch neue Namen zu geben.

Die letzte Kodeänderung dieser Übung führen Sie so durch, als hätten Sie bereits eine Version des greetings-Moduls veröffentlicht. Anstatt die Hello-Funktion zu ändern, fügen Sie eine neue Funktion Hellos hinzu, die eine Menge mit Namen entgegennimmt. Der Einfachheit halber soll die neue Funktion die bisherige benutzen. So bleibt die ursprüngliche für die schon vorhandenen Rufer erhalten (und für zukünftige, die nur einen Gruß brauchen), und die neue bedient Rufer, die die erweiterte Fähigkeit nutzen wollen.

  1. Ändern Sie greetings/greetings.go so, dass Ihr Kode wie dieser hier ausschaut:
    package greetings
    
    import (
        "errors"
        "fmt"
        "math/rand"
        "time"
    )
    
    // Hello gibt einen Gruß an die genannte Person zurück.
    func Hello(name string) (string, error) {
        // Wenn kein Name, dann Fehler mit Fehlermeldung zurückgeben.
        if name == "" {
            return "", errors.New("Name leer")
        }
        // Erzeugen eines Grußes in zufälliger ausgewählter Form.
        message := fmt.Sprintf(randomFormat(), name)
        return message, nil
    }
    
    // Hellos gibt eine Map zurück, in der alle genannten Personen
    // mit einer Grußbotschaft verknüpft sind.
    func Hellos(names []string) (map[string]string, error) {
        // Eine Map, um Namen mit Grüßen zu verknüpfen.
        messages := make(map[string]string)
        // Iterieren über das mitgegebene Slice mit den Namen und
        // und Rufen der Hello-Funktion für jeden Namen.
        for _, name := range names {
            message, err := Hello(name)
            if err != nil {
                return nil, err
            }
            // Verknüpfen von Name und Gruß in der Map.
            messages[name] = message
        }
        return messages, nil
    }
    
    // init versorgt Anfangswerte von Variablen,
    // die später in Funktionen benutzt werden.
    func init() {
        rand.Seed(time.Now().UnixNano())
    }
    
    // randomFormat gibt aus einer Menge von Grußformeln
    // eine zurück; sie wird zufällig ausgewählt.
    func randomFormat() string {
        // Ein Slice von Formatstrings mit Grußformel.
        formats := []string{
            "Hi %v. Wie geht's.",
            "Schön dich zu sehen, %v.",
            "%v! Habe die Ehre!",
        }
        // Rückgabe einer zufällig ausgewählten Grußformel.
        return formats[rand.Intn(len(formats))]
    }
    

    In diesem Kode tun Sie folgendes:

    • Sie fügen eine Hellos-Funktion hinzu, dessen Parameter ein Slice mit Namen anstelle des einzelnen Namens ist. Sie ändern weiterhin den Typ des ersten Ergebnistyps von einem String zu einer Map, damit Sie die Namen verknüpft mit den jeweiligen Grußbotschaften zurückgeben können.
    • Sie lassen die neue Hellos-Funktion die schon bestehende Hello-Funktion rufen. Damit stehen beide Funktionen zur Verfügung.
    • Sie erzeugen für die Grüße eine Map, die jeden der Namen (als Schlüssel) mit dem erzeugten Gruß (als Wert) verknüpft. In Go initialisiert man eine solche Map mit der folgenden Sntax: make(map[Schlüsseltyp]Wertetyp). Lassen Sie Hellos diese Map an den Rufer zurückgeben.
    • Sie iterieren über alle der Funktion mitgegebenen Namen, stellen für jeden sicher, dass sein Wert nicht-leer ist, und verknüpfen den Namen mit einer Grußbotschaft. In der for-Schleife gibt range zwei Werte zurück: den Index und den Wert des aktuellen Elements. Den Index brauchen Sie nicht, also nutzen Sie den Leeren Bezeichner (de), (ein Unterstrich), um ihn zu ignorieren.
  2. In Ihrem hello/hello.go übergeben Sie ein Slice mit Namen und geben dann den Inhalt der zurückerhaltenen Map aus.

    Ändern Sie in hello.go den Kode so, dass er wie hier aussieht:

    package main
    
    import (
        "fmt"
        "log"
    
        "example.com/greetings"
    )
    
    func main() {
        // Festlegen der Eigenschaften des Standard-Loggers:
        // ein Präfix für den Log-Eintrag und ein Schalter, der die
        // Ausgabe von Zeit, Quelldateiname und Zeilennummer unterbindet.
        log.SetPrefix("greetings: ")
        log.SetFlags(0)
    
        // Ein Slice mit Namen drin.
        names := []string{"Rosa", "Klara", "Karl"}
    
        // Anfordern der Grußnachrichten für die Namen.
        messages, err := greetings.Hellos(names)
        if err != nil {
            log.Fatal(err)
        }
    
        // Wenn ohne Fehler, Ausgabe der Map auf die Konsole.
        fmt.Println(messages)
    }
    

    Mit diesem Änderungen tun Sie folgendes:

    • Sie legen eine Variable names als Slice-Typ mit drei Namen an.
    • Sie übergeben die names-Variable als Argument an die Hellos-Funktion.
  3. Um zu kontrollieren, dass der Kode auch funktioniert, wechseln Sie auf der Kommandozeile in den Ordner hello und führen dort mit go run die Datei hello.go aus.

    Die Ausgabe ist eine Textdarstellung der Map, worin Namen mit Grüßen verknüpft sind, etwa so:

    $ go run hello.go
    map[Karl:Karl! Habe die Ehre! Klara:Hi Klara. Wie geht's. Rosa:Schön dich zu sehen, Rosa.]
    

Diese Lektion stellte Ihnen Maps vor, um damit Schlüssel/Wert-Paare abzubilden. Sie zeigte außerdem den Gedanken vom Erhalt der Rückwärtskompatibilität auf. Im nächsten Schritt werden Sie die Standardmethode eines Komponententests für Ihren Kode kennenlernen.

6. Erstellen eines Tests

Nun, da Ihr Kode in einen stabilen Zustand ist (Gut gemacht!), fügen Sie noch einen Test an. Testen während des Entwickelns kann frühzeitig Fehler sichtbar machen, die sich mit den Änderungen eingeschlichen haben. In dieser Lektion erstellen Sie einen Test für die Hello-Funktion.

Go's Standardverfahren für Komponententests macht es einfach, ganz nebenbei zu testen. Mit ein paar Namenskonventionen, mit Go's testing-Paket und dem go test-Kommando können Sie Tests im Nu schreiben und ausführen.

  1. Erstellen Sie im greetings-Ordner eine Datei mit Namen greetings_test.go.

    Endet der Dateiname auf _test.go, so signalisiert das dem go-Kommando, dass diese Datei Testfunktionen enthält.

  2. Fügen Sie folgenden Kode in greetings_test.go ein und sichern die Datei.
    package greetings
    
    import (
        "testing"
        "regexp"
    )
    
    // TestHelloName ruft greetings.Hello mit einem Namen
    // und prüft auf einen gültigen Rückgabewert.
    func TestHelloName(t *testing.T) {
        name := "Kurt"
        want := regexp.MustCompile(`\b`+name+`\b`)
        msg, err := Hello("Kurt")
        if !want.MatchString(msg) || err != nil {
            t.Fatalf(`Hello("Kurt") = %q, %v, erwartet: %#q, nil`, msg, err, want)
        }
    }
    
    // TestHelloEmpty ruft greetings.Hello mit einem leeren String
    // und prüft, ob auch ein Fehler zurückgegeben wurde.
    func TestHelloEmpty(t *testing.T) {
        msg, err := Hello("")
        if msg != "" || err == nil {
            t.Fatalf(`Hello("") = %q, %v, erwartet: "", error`, msg, err)
        }
    }
    

    In diesem Kode tun Sie folgendes:

    • Sie implementieren die Testfunktionen im selben Paket wie den zu testenden Kode.
    • Sie erstellen zwei Testfunktionen zum Testen der Funktion greetings.Hello. Testfunktionen haben Namen der Form TestName, wobei Name den Test bezeichnet. Außerdem erwarten Testfunktionen als Parameter einen Zeiger auf testing.T aus dem testing-Paket. Diesen Parameter brauchen Sie in Ihrem Test für Meldungen und zum Protokollschreiben.
    • Sie implementieren zwei Tests:
      • TestHelloName ruft die Hello- Funktion mit einem Wert name, zu dem die Funktion eine gültige Nachricht zurückliefern soll. Sollte die Funktion einen Fehler oder eine unerwartete Nachricht (eine, die den Namen nicht enthält) zurückgeben, benutzen Sie die Methode Fatalf des Parameters t, um eine Meldung auf der Konsole auszugeben und die Testausführung zu beenden.
      • TestHelloEmpty ruft die Hello- Funktion mit einem leeren String. Dieser Test soll bestätigen, dass Ihre Fehlerbehandlung funktioniert. Sollte die Funktion einen nicht-leeren String oder keinen Fehler zurückgeben, benutzen Sie die Methode Fatalf des Parameters t, um eine Meldung auf der Konsole auszugeben und die Testausführung zu beenden.
  3. Zum Ausführen der Tests starten Sie von der Kommandozeile aus dem greetings-Ordner heraus das Kommando go test.

    Das Kommando go test führt Testfunktionen (also solche, deren Namen mit Test beginnen) aus Testdateien (also deren Namen mit _test.go enden) aus. Sie können auch den Schalter -v (verbose) benutzen, um eine wortreiche Ausgabe mit der Liste aller Tests und deren Ergebnisse zu erhalten.

    Die Tests sollten erfolgreich sein.

    $ go test
    PASS
    ok      example.com/greetings   0.364s
    
    $ go test -v
    === RUN   TestHelloName
    --- PASS: TestHelloName (0.00s)
    === RUN   TestHelloEmpty
    --- PASS: TestHelloEmpty (0.00s)
    PASS
    ok      example.com/greetings   0.372s
    
  4. Pfuschen Sie nun in der greetings.Hello-Funktion, um mal einen scheiternden Test zu Gesicht zu bekommen.

    Die Testfunktion TestHelloName prüft den Rückgabewert für den der Hello-Funktion mitgegebenen Namen. Um ein Scheitern beobachten zu können, ändern Sie die Funktion greetings.Hello so, dass ihr Ergebnis nicht länger den Namen enthält.

    Ändern Sie die Hello-Funktion in greetings/greetings.go wie unten zu sehen. Beachten Sie, dass die hervorgehobenen Zeilen den Rückgabewert der Funktion so ändern, als wäre das Argument name versehenlich entfernt worden.

    // Hello gibt einen Gruß an die genannte Person zurück.
    func Hello(name string) (string, error) {
        // Wenn kein Name, dann Fehler mit Fehlermeldung zurückgeben.
        if name == "" {
            return "", errors.New("Name leer")
        }
        // Erzeugen eines Grußes in zufälliger ausgewählter Form.
        // message := fmt.Sprintf(randomFormat(), name)
        message := fmt.Sprint(randomFormat())
        return message, nil
    }
    
  5. Zum Ausführen der Tests starten Sie von der Kommandozeile aus dem greetings-Ordner heraus das Kommando go test.

    Sie starten go test dieses Mal ohne den Schalter -v. Die Ausgabe wird so nur Einzelheiten für die gescheiterten Tests enthalten, was übersichtlicher ist, wenn Sie viele Tests haben. TestHelloName sollte scheitern, TestHelloEmpty bleibt erfolgreich.

    $ go test
    --- FAIL: TestHelloName (0.00s)
        greetings_test.go:15: Hello("Kurt") = "Hi %v. Wie geht's.", <nil>, erwartet: `\bKurt\b`, nil
    FAIL
    exit status 1
    FAIL    example.com/greetings   0.182s
    

Diese Lektion stellte Go's Standardverfahren für Komponententests vor. Im nächsten Schritt werden Sie erfahren, wie man kompiliert und installiert, um den Kode lokal ausführen zu können.

7. Fertigen und Installieren der Anwendung

In dieser letzten Lektion lernen Sie ein neues go-Kommando kennen. Das bisher benutzte Kommando go run mag als Abkürzung nützlich sein, wenn ein Eine-Datei-Programm umgewandelt und gestartet werden soll, es erzeugt aber keine ausführbare Binärdatei, die man einfach wiederholt starten könnte. Wenn Sie das wollen, ist das Kommando go install eine gute Wahl, denn es wandelt ihren Kode um und installiert die erzeugte Binärdatei dort, wo Sie sie starten können.

  1. Wechseln Sie auf der Kommandozeile in den Ordner, der hello.go enthält.
  2. Finden Sie den Go-Installationspfad, also wo das go-Kommando das aktuelle Paket installieren wird.

    Den Installationspfad kann man ermitteln, indem man ein go list-Kommando der folgenden Form ausführt:

    $ go list -f '{{.Target}}'
    

    Die Ausgabe ergibt beispielsweise /home/gopher/bin/hello, was bedeutet, dass Binärdateien nach /home/gopher/bin installiert werden. Diese Angabe brauchen Sie für den nächsten Schritt.

  3. Ergänzen Sie den Suchpfad Ihres Systems um den oben ermittelten Go-Installationspfad.

    So braucht ein Aufruf dann keine Angabe über den Ort Ihrer Binärdatei mehr.

    • Unter Linux oder Mac führen Sie folgendes Kommando aus:
      $ export PATH=$PATH:/path/to/your/install/directory
      
    • Unter Windows führen Sie folgendes Kommando aus:
      set PATH=%PATH%;C:\path\to\your\install\directory
      

    Alternativ: Wenn bereits ein Ordner für Binärdateien wie $HOME/bin in Ihrem Suchpfad existiert, und wenn Sie dort Ihre Go-Programme installieren wollen, dann können Sie für Go das Installationsziel durch Setzen der GOBIN-Variablen ändern. Dazu nutzen Sie das Kommando go env in der folgenden Form:

    $ go env -w GOBIN=/path/to/your/bin
    

    oder

    go env -w GOBIN=C:\path\to\your\bin
    
  4. Nachdem Sie den Suchpfad aktualisiert haben, starten Sie das Kommando go install zum Kompilieren und Installieren des Pakets.
    $ go install
    
  5. Starten Sie jetzt Ihre Anwendung ganz einfach mit deren Namen.
    $ hello
    map[Karl:Karl! Habe die Ehre! Klara:Hi Klara. Wie geht's. Rosa:Rosa! Habe die Ehre!.]
    

Damit ist diese Übung zu Ende. Ein nächster Schritt, der viele weitere Eigenschaften von Go präsentiert, wäre die Rundreise durch Go, zu finden unter Tour of Go.