Dénomination correcte des packages pour les tests avec le langage Go

102

J'ai vu plusieurs stratégies de nommage de packages de test différentes dans Go et je voulais savoir quels sont les avantages et les inconvénients de chacune et laquelle je devrais utiliser.

Stratégie 1:

Nom de fichier: github.com/user/myfunc.go

package myfunc

Nom du fichier de test: github.com/user/myfunc_test.go

package myfunc

Voir bzip2 pour un exemple.

Stratégie 2:

Nom de fichier: github.com/user/myfunc.go

package myfunc

Nom du fichier de test: github.com/user/myfunc_test.go

package myfunc_test

import (
    "github.com/user/myfunc"
)

Voir fil pour un exemple.

Stratégie 3:

Nom de fichier: github.com/user/myfunc.go

package myfunc

Nom du fichier de test: github.com/user/myfunc_test.go

package myfunc_test

import (
    . "myfunc"
)

Voir les chaînes pour un exemple.

La bibliothèque standard Go semble utiliser un mélange de stratégies 1 et 2. Laquelle des trois devrais-je utiliser? C'est une douleur d'ajouter package *_testà mes packages de test car cela signifie que je ne peux pas tester les méthodes privées de mon package, mais peut-être qu'il y a un avantage caché dont je ne suis pas conscient?

Dan
la source
9
Cette question ne fera que susciter des opinions divergentes, mais je vais ajouter la mienne. Vous ne devriez pas avoir besoin de tester vos méthodes privées. Vous souhaitez tester l'interface de votre package que d'autres développeurs utiliseront. Si les tests échouent, vous savez que vos méthodes privées doivent être examinées.
Brenden
2
L'exemple [wire] ( github.com/btcsuite/btcd/blob/master/wire/msgtx_test.go ) pour la stratégie 2, est en fait maintenant aussi un exemple de la stratégie 1 ...
durp

Réponses:

130

La différence fondamentale entre les trois stratégies que vous avez répertoriées est de savoir si le code de test se trouve ou non dans le même package que le code testé. La décision d'utiliser package myfuncou package myfunc_testdans le fichier de test dépend de si vous souhaitez effectuer des tests en boîte blanche ou en boîte noire .

Il n'y a rien de mal à utiliser les deux méthodes dans un projet. Par exemple, vous pourriez avoir myfunc_whitebox_test.goet myfunx_blackbox_test.go.

Comparaison des packages de code de test

  • Test de la boîte noire: utilisez package myfunc_test, qui garantira que vous n'utilisez que les identifiants exportés .
  • Test de la boîte blanche: à utiliser package myfuncpour avoir accès aux identifiants non exportés. Idéal pour les tests unitaires qui nécessitent l'accès à des variables, des fonctions et des méthodes non exportées.

Comparaison des stratégies énumérées en question

  • Stratégie 1: Le fichier myfunc_test.goutilise package myfunc- Dans ce cas, le code de test dans myfunc_test.gosera dans le même package que le code testé dans myfunc.go, qui est myfuncdans cet exemple.
  • Stratégie 2: Le fichier myfunc_test.goutilise package myfunc_test- Dans ce cas, le code de test dans myfunc_test.go"sera compilé en tant que package séparé, puis lié et exécuté avec le binaire de test principal." [Source: lignes 58 à 59 du code source de test.go ]
  • Stratégie 3: Le fichier myfunc_test.goutilise package myfunc_testmais importe en myfuncutilisant la notation par points - Ceci est une variante de la stratégie 2, mais utilise la notation par points pour importer myfunc.
Matthew Rankin
la source
1
Il convient de noter que l'utilisation de la stratégie 1 conservera également les fichiers _test.goséparés du package testé (le même comportement que la stratégie 2). Cela ne semble pas être documenté par github.com/golang/go/issues/15315
Kevin Deenanauth
J'ai vu un package fort utiliser la stratégie 3, mais je ne comprends pas à quoi ça sert?
PickBoy
1
J'ai forké un package et apporté des modifications, et maintenant mes tests essaient tous d'importer le dépôt d'origine au lieu de mon package forké. Avec la stratégie 3, je n'ai pas à changer le "github.com/original/link" en "github.com/my/fork", car il ne fait que référencer "." au lieu.
nmarley
1
@KevinDeenanauth Cela m'a surpris. Je pensais avoir trouvé un piège quand je viens de trouver un _test.goavec un _testnom non- package contenant un func init()qui modifie une variable globale de package pour les tests. J'avais tort.
Zyl
1
@nmarley .ne résout pas votre problème de fork. Ce n'est pas une importation relative. Il importe simplement les identifiants "dans le package courant".
qaisjp
19

Cela dépend de la portée de vos tests. Les tests de haut niveau (intégration, acceptation, etc.) doivent probablement être placés dans un package séparé pour s'assurer que vous utilisez le package via l'API exportée.

Si vous avez un paquet volumineux avec de nombreux composants internes qui doivent être testés, utilisez le même package pour vos tests. Mais ce n'est pas une invitation à vos tests d'accéder à un quelconque état privé. Cela ferait du refactoring un cauchemar. Quand j'écris des structures dans go, j'implémente souvent des interfaces. Ce sont ces méthodes d'interface que j'appelle à partir de mes tests, pas toutes les méthodes / fonctions d'aide individuellement.

mdwhatcott
la source
13

Vous devez utiliser la stratégie 1 chaque fois que possible. Vous pouvez utiliser le foo_testnom de package spécial pour éviter les cycles d'importation, mais c'est principalement là pour que la bibliothèque standard puisse être testée avec le même mécanisme. Par exemple, stringsne peut pas être testé avec la stratégie 1 car le testingpackage dépend de strings. Comme vous l'avez dit, avec la stratégie 2 ou 3, vous n'avez pas accès aux identifiants privés du paquet, il est donc généralement préférable de ne pas l'utiliser sauf si vous y êtes obligé.

Guelfey
la source
10
Comment ne pas avoir accès aux identifiants privés dans les tests n'est-il pas une vertu?
jub0bs
3
selon les bonnes pratiques de test, vous ne testez pas les détails d'implémentation internes pour un artefact de code; faire son fils, est une odeur de code
Gerardo Lima
0

Une note importante que j'aimerais ajouter à propos import .de Golang CodeReviewComments :

Le import .formulaire peut être utile dans les tests qui, en raison de dépendances circulaires , ne peuvent pas être intégrés au package testé:

package foo_test

import (
    "bar/testutil" // also imports "foo"
    . "foo"
)

Dans ce cas, le fichier de test ne peut pas être dans le package foo car il utilise bar/testutil, qui importe foo. Nous utilisons donc «l'importation». form pour laisser le fichier faire semblant de faire partie du package foo même s'il ne l'est pas.

Sauf dans ce cas, ne l'utilisez pasimport . dans vos programmes. Cela rend les programmes beaucoup plus difficiles à lire car il n'est pas clair si un nom comme Quux est un identifiant de premier niveau dans le package actuel ou dans un package importé.

Eric
la source