¿Cómo manejar adecuadamente los parámetros globales para las pruebas unitarias en python?

11

Estamos implementando muchos algoritmos que generalmente tienen muchos parámetros compartidos, conocidos públicamente y relevantes para la seguridad.

Actualmente, simplemente usamos una clase que contiene todos los parámetros y dos objetos globales predefinidos:

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)  

Los algoritmos luego toman un PublicParamsobjeto como argumento que por defecto es productivopublicParams_secure

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

y

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

De esta manera, todavía podemos inyectar diferentes parámetros públicos para una prueba de unidad más fácil:

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

Lo que no me gusta de este enfoque:

  • Darle publicParamsun valor predeterminado lo hace opcional al llamar a algún algoritmo. Sin embargo, se hace fácil olvidar pasarlo cuando se llama AlgoTwodesde dentro AlgoOne, lo que daría como resultado que se utilicen dos objetos diferentes si el objeto de prueba se pasó aAlgoOne

¿Existe una mejor manera que sea menos propensa pero que aún ofrezca flexibilidad para las pruebas unitarias? ¿Es esta realmente la mejor práctica?

netik
fuente

Respuestas:

1

Crear archivos de configuración test_config.pyy production_config.py. Seleccione uno de ellos utilizando la variable de entorno o un argumento de línea de comandos. Importarlo (o leer / analizar, si elige .json/ en .txtlugar de .py), y hacer que el resultado esté disponible para todo el programa a través de un objeto global en un módulo que puede importar en cualquier lugar.

Esto es muy similar a lo que ya está haciendo, excepto que lo lleva un paso más allá, desde el alcance global hasta el shell desde el que invoca Python. La ventaja es que ya no existe el riesgo de mezclar accidentalmente la producción y la configuración de prueba: no puede leer ambos archivos en la misma sesión de Python, ya que solo hay una variable de entorno / línea de comando.

max
fuente
0

Hay varias cosas que puedes hacer.

  • Deja de usar globals
  • Dejar de usar valores predeterminados
  • Siempre pruebe a través de métodos de ayuda privados que no permitan el uso de los valores predeterminados

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

Seguro que cualquiera de esas opciones es mucho trabajo, pero no te preguntarías si esto no fuera un problema para ti.

naranja confitada
fuente
0

Uno siempre podría separar la colección de valores del contexto global y el procesamiento de esos parámetros.

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
usuario166988
fuente