Wie gehe ich mit globalen Parametern für Unit-Tests in Python richtig um?

11

Wir implementieren viele Algorithmen, die normalerweise viele gemeinsame, öffentlich bekannte und sicherheitsrelevante Parameter aufweisen.

Derzeit verwenden wir einfach eine Klasse, die alle Parameter und zwei vordefinierte globale Objekte enthält:

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)  

Die Algorithmen nehmen dann ein PublicParamsObjekt als Argument, das standardmäßig produktiv istpublicParams_secure

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

und

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

Auf diese Weise können wir immer noch verschiedene öffentliche Parameter einfügen, um das Testen von Einheiten zu vereinfachen:

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

Was ich an diesem Ansatz nicht mag:

  • Wenn Sie publicParamseinen Standardwert angeben, ist dieser beim Aufrufen eines Algorithmus optional. Es wird jedoch leicht vergessen, das Übergeben zu vergessen, wenn AlgoTwovon innen aufgerufen wird AlgoOne, was dazu führen würde, dass zwei verschiedene Objekte verwendet werden, wenn das Testobjekt an übergeben wirdAlgoOne

Gibt es einen besseren Weg, der weniger anfällig ist, aber dennoch Flexibilität für Unit-Tests bietet? Ist das wirklich die beste Praxis?

netik
quelle

Antworten:

1

Konfigurationsdateien erstellen test_config.pyund production_config.py. Wählen Sie eine davon mit der Umgebungsvariablen oder einem Befehlszeilenargument aus. Importieren Sie es (oder lesen / analysieren Sie, wenn Sie .json/ .txtanstelle von wählen .py) und stellen Sie das Ergebnis dem gesamten Programm über ein globales Objekt in einem Modul zur Verfügung, das Sie überall importieren können.

Dies ist sehr ähnlich zu dem, was Sie bereits tun, außer dass es einen Schritt weiter geht, vom globalen Bereich bis zur Shell, von der aus Sie Python aufrufen. Der Vorteil ist, dass nicht mehr die Gefahr besteht, dass die Produktions- und Testkonfiguration versehentlich verwechselt wird: Sie können nicht beide Dateien in derselben Python-Sitzung lesen, da nur eine Umgebungsvariable / Befehlszeile vorhanden ist.

max
quelle
0

Es gibt eine Reihe von Dingen, die Sie tun können.

  • Verwenden Sie keine Globals mehr
  • Verwenden Sie keine Standardeinstellungen mehr
  • Testen Sie immer private Hilfsmethoden, bei denen die Standardeinstellungen nicht verwendet werden können

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

Sicher, jede dieser Optionen ist viel Arbeit, aber Sie würden nicht fragen, ob dies kein Problem für Sie ist.

candied_orange
quelle
0

Man könnte immer die Sammlung von Werten vom globalen Kontext und die Verarbeitung dieser Parameter trennen.

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
quelle