Comment gérer correctement les paramètres globaux pour les tests unitaires en python?

11

Nous mettons en œuvre de nombreux algorithmes qui comportent généralement de nombreux paramètres partagés, connus du public et liés à la sécurité.

Actuellement, nous utilisons simplement une classe contenant tous les paramètres et deux objets globaux prédéfinis:

class PublicParams(object):
    p = q = 0

    def __init__(self, p, q):
        self.p = p
        self.q = q

# used for tests
publicParams_test = PublicParams(15,7)               

# Some 2048 bit numbers for example
publicParams_secure = PublicParams(128378947298374928374,128378947298374928374)  

Les algorithmes prennent ensuite un PublicParamsobjet comme un argument qui par défaut est productifpublicParams_secure

def AlgoOne(n, publicParams = publicParams_secure):
    # do stuff with publicParams.p
    # ...
    AlgoTwo(x, publicParams)

et

def AlgoTwo(x, publicParams= publicParams_secure):
    # do stuff with publicParams.q

De cette façon, nous pouvons toujours injecter différents paramètres publics pour faciliter les tests unitaires:

class AlgoOneTest(unittest.TestCase):
    def test(self):
        # compare with manually computed result
        self.assertTrue(AlgoOne(1, publicParams_test) == 10) 

Ce que je n'aime pas dans cette approche:

  • Donner publicParamsune valeur par défaut la rend facultative lors de l'appel d'un algorithme. Cependant, il devient facile d'oublier de le passer lors d'un appel AlgoTwode l'intérieur AlgoOne, ce qui entraînerait l'utilisation de deux objets différents si l'objet de test était passé àAlgoOne

Existe-t-il un meilleur moyen qui est moins sujet mais qui offre toujours une flexibilité pour les tests unitaires? Est-ce vraiment la meilleure pratique?

netik
la source

Réponses:

1

Créez des fichiers de configuration test_config.pyet production_config.py. Sélectionnez l'un d'eux à l'aide de la variable d'environnement ou d'un argument de ligne de commande. Importez-le (ou lisez / analysez, si vous choisissez .json/ .txtau lieu de .py), et rendez le résultat disponible pour l'ensemble du programme via un objet global dans un module que vous pouvez importer n'importe où.

Ceci est très similaire à ce que vous faites déjà, sauf qu'il va un peu plus loin, de la portée globale au shell à partir duquel vous appelez python. L'avantage est qu'il n'y a plus de risque de mélanger accidentellement la configuration de production et de test: vous ne pouvez pas lire les deux fichiers dans la même session python, car il n'y a qu'une seule variable d'environnement / ligne de commande.

max
la source
0

Il y a plusieurs choses que vous pouvez faire.

  • Arrêtez d'utiliser les globaux
  • Ne plus utiliser les valeurs par défaut
  • Toujours tester via des méthodes d'assistance privées qui ne permettent pas l'utilisation des valeurs par défaut

    def _AlgoOne(n, publicParams):
        return AlgoOne(n, publicParams)
    

Bien sûr, l'une de ces options demande beaucoup de travail, mais vous ne vous demanderiez pas si cela ne vous posait pas de problème.

candied_orange
la source
0

On pourrait toujours séparer la collecte de valeurs du contexte global et le traitement de ces paramètres.

def do_the_thing():
    """Provides the public (rather untestable) context.
    _do_the_thing(global1, global2, publicParams)"""

def _do_the_thing(blah, blah, blah):
    "Actually does the thing"
    pass
user166988
la source