Jak poprawnie obsługiwać parametry globalne do testowania jednostkowego w Pythonie?

11

Wdrażamy wiele algorytmów, które zazwyczaj mają wiele wspólnych, znanych publicznie i istotnych dla bezpieczeństwa parametrów.

Obecnie używamy po prostu klasy zawierającej wszystkie parametry i dwa predefiniowane obiekty globalne:

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)  

Algorytmy przyjmują następnie PublicParamsobiekt jako argument domyślnie produktywnypublicParams_secure

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

i

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

W ten sposób możemy nadal wprowadzać różne parametry publiczne, aby ułatwić testowanie jednostek:

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

Czego nie lubię w tym podejściu:

  • Podanie publicParamswartości domyślnej czyni ją opcjonalną podczas wywoływania jakiegoś algorytmu. Łatwo jednak zapomnieć o przekazaniu go podczas wywoływania AlgoTwood wewnątrz AlgoOne, co spowodowałoby użycie dwóch różnych obiektów, gdyby obiekt testowy został przekazany doAlgoOne

Czy istnieje lepszy sposób, który jest mniej podatny, ale nadal oferuje elastyczność w testowaniu jednostkowym? Czy to naprawdę najlepsza praktyka?

netik
źródło

Odpowiedzi:

1

Utwórz pliki konfiguracyjne test_config.pyi production_config.py. Wybierz jeden z nich, używając zmiennej środowiskowej lub argumentu wiersza poleceń. Zaimportuj go (lub przeczytaj / parsuj, jeśli wybierzesz .json/ .txtzamiast .py) i udostępnij wynik całemu programowi za pośrednictwem obiektu globalnego w module, który możesz importować w dowolnym miejscu.

Jest to bardzo podobne do tego, co już robisz, z tym wyjątkiem, że idzie o krok dalej, od zakresu globalnego do powłoki, z której wywołujesz Python. Zaletą jest to, że nie ma już ryzyka przypadkowego pomieszania konfiguracji produkcyjnej i testowej: nie można odczytać obu plików w tej samej sesji Pythona, ponieważ istnieje tylko jedna zmienna środowiskowa / wiersz poleceń.

max
źródło
0

Istnieje wiele rzeczy, które możesz zrobić.

  • Przestań używać globałów
  • Przestań używać ustawień domyślnych
  • Zawsze testuj metody prywatnego pomocnika, które nie pozwalają na użycie ustawień domyślnych

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

Oczywiście, każda z tych opcji to dużo pracy, ale nie pytasz, czy to nie był problem.

candied_orange
źródło
0

Zawsze można oddzielić zbiór wartości od kontekstu globalnego i przetwarzanie tych parametrów.

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
użytkownik166988
źródło