Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
dev:go:tutoriels:ecrire_un_module_en_go [2023/08/03 16:47] – yoann | dev:go:tutoriels:ecrire_un_module_en_go [2023/08/06 10:13] (Version actuelle) – yoann | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
{{tag> | {{tag> | ||
- | |||
- | :TODO: | ||
====== Écrire un module en Go ====== | ====== Écrire un module en Go ====== | ||
Ligne 215: | Ligne 213: | ||
log.SetFlags((0)) | log.SetFlags((0)) | ||
- | msg, err := greetings.Hello(" | + | msg, err := greetings.Hello("" |
//Si une erreur est retournée, elle est affichée dans la console | //Si une erreur est retournée, elle est affichée dans la console | ||
Ligne 227: | Ligne 225: | ||
} | } | ||
</ | </ | ||
+ | |||
+ | Dans le code ci-dessus on a : | ||
+ | * Configurer le paquetage " | ||
+ | * Assigné les deux valeurs retournées par Hello à des variables ; | ||
+ | * Modifié l' | ||
+ | * Testé la valeur de l' | ||
+ | * Utilisé les fonctions du paquetage " | ||
+ | |||
+ | Depuis la ligne de commande, on peut exécuter notre code: | ||
+ | |||
+ | <code bash> | ||
+ | go run . | ||
+ | greetings: empty name | ||
+ | exit status 1 | ||
+ | </ | ||
+ | |||
+ | ===== Retourner un message aléatoire ===== | ||
+ | |||
+ | On va modifier un peu le code pour retourner un message aléatoirement parmi un petit ensemble de messages. Pour cela on va utiliser une Go slice. Une slice peut être vu comme un tableau avec la particularité de pouvoir changer de taille dynamiquement lors des ajouts/ | ||
+ | |||
+ | Nous allons ajouter une slice contenant trois messages et le code permettant d'en retourner aléatoirement un parmi les trois. | ||
+ | |||
+ | On ouvre l' | ||
+ | |||
+ | <code go greetings.go> | ||
+ | package greetings | ||
+ | |||
+ | import ( | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | |||
+ | // Hello retourne un message de bienvenue à la personne désignée | ||
+ | func Hello(name string) (string, error) { | ||
+ | //Si aucun nom n'est donné retourne une erreur avec un message. | ||
+ | if name == "" | ||
+ | return "", | ||
+ | } | ||
+ | |||
+ | message := fmt.Sprintf(randomFormat(), | ||
+ | return message, nil | ||
+ | } | ||
+ | |||
+ | // randomFormat retourne aléatoirement un message préformatté parmi l' | ||
+ | func randomFormat() string { | ||
+ | formats := []string{ | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | |||
+ | //retourne aléatoirement un format de message | ||
+ | return formats[rand.Intn(len(formats))] | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Quelques remarques: | ||
+ | * La fonction randomFormat() commence par une minuscule la rendant accessible seulement par le code de son propre paquetage (en d' | ||
+ | * La fonction randomFormat() déclare une slice '' | ||
+ | * On utilise le paquetage " | ||
+ | * Dans la fonction Hello() on appelle maintenant randomFormat() pour obtenir un message aléatoire. | ||
+ | |||
+ | |||
+ | Le fichier hello.go est légèrement modifié : on remplace la chaîne vide par une chaîne de caractères quelconque pour retrouver le comportement normal du programme. | ||
+ | |||
+ | <code bash> | ||
+ | yoann@node-7c87: | ||
+ | Hello, Yoann. Bienvenue! | ||
+ | yoann@node-7c87: | ||
+ | Heureux de vous revoir Yoann! | ||
+ | yoann@node-7c87: | ||
+ | Heureux de vous revoir Yoann! | ||
+ | yoann@node-7c87: | ||
+ | Heureux de vous revoir Yoann! | ||
+ | yoann@node-7c87: | ||
+ | Hello, Yoann. Bienvenue! | ||
+ | </ | ||
+ | |||
+ | ===== Retourner un message à plusieurs personnes ===== | ||
+ | |||
+ | Nous allons modifier une dernière fois nos modules afin d' | ||
+ | |||
+ | Ces modifications soulèvent une problématique : modifier les paramètres de la fonction Hello() change sa signature. Si le module a déjà été publié et qu'il est utilisé, ces changements conduiront à une à des erreurs dans les programmes appelants. | ||
+ | |||
+ | Dans ce cas une meilleur option est d' | ||
+ | |||
+ | Via l' | ||
+ | <code go greetings.go> | ||
+ | package greetings | ||
+ | |||
+ | import ( | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | |||
+ | // Hello retourne un message de bienvenue à la personne désignée | ||
+ | func Hello(name string) (string, error) { | ||
+ | //Si aucun nom n'est donné retourne une erreur avec un message. | ||
+ | if name == "" | ||
+ | return "", | ||
+ | } | ||
+ | |||
+ | message := fmt.Sprintf(randomFormat(), | ||
+ | return message, nil | ||
+ | } | ||
+ | |||
+ | // HelloAll retourne un map associant un message de bienvenue à chaque personne en entrée | ||
+ | func HelloAll(names []string) (map[string]string, | ||
+ | //un map pour associer un message à chaque nom | ||
+ | messages := make(map[string]string) | ||
+ | |||
+ | //boucle sur la slice en entrée (les noms) et appelle Hello() pour associer une message | ||
+ | for _, name := range names { | ||
+ | //pour chaque index(non utilisé _), valeur du paramètre ' | ||
+ | msg, err := Hello(name) | ||
+ | |||
+ | if err != nil { | ||
+ | //une erreur s'est produite, on retourne un objet vide et le code erreur | ||
+ | return nil, err | ||
+ | } | ||
+ | |||
+ | messages[name] = msg | ||
+ | } | ||
+ | |||
+ | return messages, nil | ||
+ | } | ||
+ | |||
+ | // randomFormat retourne aléatoirement un message préformatté parmi l' | ||
+ | func randomFormat() string { | ||
+ | formats := []string{ | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | |||
+ | //retourne aléatoirement un format de message | ||
+ | return formats[rand.Intn(len(formats))] | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Dans ce code nous avons: | ||
+ | * Ajouté une fonction HelloAll() ayant en paramètre une slice de plusieurs noms et retournant un map permettant d' | ||
+ | * La nouvelle fonction HelloAll() appelle la fonction Hello(). Cet usage des fonction est aussi désigné factorisation et réduit les risques d' | ||
+ | * Créé une variable " | ||
+ | * Exécuter une boucle sur chaque élément de la slice " | ||
+ | |||
+ | Dans notre fichier hello.go nous pouvons à présent faire quelques changements : | ||
+ | |||
+ | <code go hello.go> | ||
+ | package main | ||
+ | |||
+ | import ( | ||
+ | " | ||
+ | " | ||
+ | |||
+ | " | ||
+ | ) | ||
+ | |||
+ | func main() { | ||
+ | // | ||
+ | log.Default().SetPrefix(" | ||
+ | // | ||
+ | //de ligne | ||
+ | log.SetFlags((0)) | ||
+ | |||
+ | // | ||
+ | names := []string{" | ||
+ | |||
+ | //Obtenir un message de bienvenue pour chaque nom | ||
+ | messages, err := greetings.HelloAll(names) | ||
+ | |||
+ | //Si une erreur est retournée, elle est affichée dans la console | ||
+ | //et le programme s' | ||
+ | if err != nil { | ||
+ | log.Fatal(err) | ||
+ | } | ||
+ | |||
+ | //Affiche le message dans la console | ||
+ | fmt.Println(messages) | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Les changements sont les suivants : | ||
+ | * Création d'une variable " | ||
+ | * Passage en argument de la variable " | ||
+ | |||
+ | Exécutons le code ainsi modifié : | ||
+ | <code bash> | ||
+ | go run . | ||
+ | map[Alfred: | ||
+ | </ | ||
+ | |||
+ | ===== Ajout d' un test unitaire ===== | ||
+ | |||
+ | Maintenant que notre code a implémenter les fonctionnalités souhaitées, | ||
+ | |||
+ | Go intègre le support des tests unitaires. Le paquetage " | ||
+ | |||
+ | * Dans le répertoire greetings, créer un fichier '' | ||
+ | * Éditer le fichier greetings_test.go avec le contenu suivant : | ||
+ | |||
+ | <code go greetings_test.go> | ||
+ | package greetings | ||
+ | |||
+ | import ( | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | |||
+ | // TestHelloName appelle Hello() avec un nom et vérifie | ||
+ | // la valeur retournée. | ||
+ | func TestHelloName(t *testing.T) { | ||
+ | aName := " | ||
+ | want := regexp.MustCompile(`\b` + aName + `\b`) | ||
+ | msg, err := Hello(" | ||
+ | |||
+ | if !want.MatchString(msg) || err != nil { | ||
+ | t.Fatalf(`Hello(" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // TestHelloEmpty appelle Helo() avec une chaine vide doit | ||
+ | // retourner une erreur. | ||
+ | func TestHelloEmpty(t *testing.T) { | ||
+ | msg, err := Hello("" | ||
+ | |||
+ | if msg != "" | ||
+ | t.Fatalf(`Hello("" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Dans ce code on a : | ||
+ | * Définit des fonctions de test dans le même paquetage que le code à tester ; | ||
+ | * Définit deux fonctions de test pour tester la fonction greetings.Hello(). Les fonctions de test doivent avoir un nom préfixé par ' | ||
+ | * A propos des deux tests: | ||
+ | * TestHelloName() appelle la fonction Hello() avec un nom en argument et vérifie que la fonction est capable de retourner un message valide. Si le retour est une erreur ou un message avec un contenu non attendu on utilise le paramètre " | ||
+ | * TestHelloEmpty() appelle la fonction Hello() avec une chaine vide. Ce test permet de valider le fonctionnement de la gestion des erreurs. Si le test retourne une chaine non vide ou ne retourne pas d' | ||
+ | |||
+ | Depuis la ligne de commande on peut lancer les tests: | ||
+ | <code powershell> | ||
+ | go test | ||
+ | </ | ||
+ | |||
+ | La commande **go test** exécute les fonctions de test (ayant des noms préfixés par " | ||
+ | |||
+ | Si on introduit (ici volontairement) une erreur lors du developpement en modifiant le comportement de la fonction Hello(), une des fonctions de test pourra révèler l' | ||
+ | |||
+ | < | ||
+ | go test -v | ||
+ | === RUN | ||
+ | greetings_test.go: | ||
+ | --- FAIL: TestHelloName (0.00s) | ||
+ | === RUN | ||
+ | --- PASS: TestHelloEmpty (0.00s) | ||
+ | FAIL | ||
+ | exit status 1 | ||
+ | FAIL example.com/ | ||
+ | </ | ||
+ | |||
+ | ===== Compiler et installer l' | ||
+ | |||
+ | Ici on aborde quelques nouvelles commandes Go. La commande go run permet de rapidement compiler et exécuter un programme lors du développement alors qu'on effectue régulièrement des modifications. | ||
+ | |||
+ | * La commande **'' | ||
+ | * La commande go **'' | ||
+ | |||
+ | Lorsque le binaire est créé avec **'' | ||
+ | |||
+ | Pour connaitre le chemin du dossier d' | ||
+ | < | ||
+ | go list -f ' | ||
+ | </ | ||
+ | |||
+ | Vérifier que le dossier d' | ||
+ | |||
+ | <code powershell> | ||
+ | echo $env:PATH | ||
+ | </ | ||
+ | |||
+ | La variable PATH doit contenir le dossier d' | ||
+ | |||
+ | Si vous souhaitez installer le binaire dans un répertoire différent de votre PATH, vous pouvez modifier/ | ||
+ | |||
+ | < | ||
+ | go env -w GOBIN=C: | ||
+ | </ | ||
+ | |||
+ | Une fois le chemin d' | ||
+ | |||
+ | |||
===== Références ===== | ===== Références ===== | ||
* https:// | * https:// |