Tengo una función pública fun(a, b). Ahora, para fines de prueba, me gustaría que fun tome un argumento adicional test_argument.

¿Cuál es la forma aceptada de hacer esto? Mis ideas:

def fun(a, b, test_argument=None):
    ...

Pero no es bonito, porque todos los que usan esta función ven el argumento test_argument, y requiere al menos un comentario.

def fun(a, b, **kwargs):
    ...
    test_argument = kwargs.get('test_argument', None)

Ahora alguien puede preguntarse para qué sirve **kwargs.

EDITAR: Quiero esto porque fun está estructurado de la siguiente manera:

def fun(a, b):
    do_locally(a, b)
    if condition(a, b):
        do_locally1(a, b)
        do_remotely_after_one_minute(a, b)
    else:
        do_locally2(a, b)
        do_remotely_after_two_minutes(a, b)

Me gustaría probar el estado de a y b después de llamar a fun(a, b) pero antes de que comience la ejecución remota. Sin embargo, en el proyecto, el código ejecutado de forma remota está incluido en las pruebas (0% de probabilidad de cambiar este comportamiento). Entonces pensé en pasar un argumento adicional:

def fun(a, b, test=False):
    do_locally(a, b)
    if condition(a, b):
        do_locally1(a, b)
        if not test:
            do_remotely_after_one_minute(a, b)
    else:
        do_locally2(a, b)
        if not test:
            do_remotely_after_two_minutes(a, b)
0
kitek 29 ago. 2014 a las 18:00

3 respuestas

La mejor respuesta

Ambas funciones do_remotely_after_* son solo nombres globales; Se pueden reemplazar temporalmente durante las pruebas:

import module_under_test

def test_fun():
    # patch remote methods with noops
    draom = module_under_test.do_remotely_after_one_minute
    module_under_test.do_remotely_after_one_minute = lambda *args: pass
    dratm = module_under_test.do_remotely_after_two_minutes
    module_under_test.do_remotely_after_two_minutes = lambda *args: pass

    module_under_test.fun(a, b)

    # restore remote methods
    module_under_test.do_remotely_after_one_minute = draom
    module_under_test.do_remotely_after_two_minutes = dratw

Podría usar un administrador de contexto para manejar el parcheo y la restauración, pero el mejor enfoque es usar el { {X0}} biblioteca para manejar eso por usted:

@mock.patch('module_under_test.do_remotely_after_one_minute')
@mock.patch('module_under_test.do_remotely_after_two_minutes')
def test_fun(mock_dratm, mock_draom):
    module_under_test.fun(a, b)

Y tener acceso a los simulacros también, por lo que puede afirmar si se han llamado correctamente.

2
Martijn Pieters 29 ago. 2014 a las 14:46

Probablemente deberías usar la biblioteca mock. Fue hecho exactamente para este personal:

>>> def do_loc(a,b):
...   return "IN do_loc %s %s" % (a, b)
... 
>>> def do_remte(a,b):
...   return "IN do_remte %s %s" % (a, b)
... 
>>> def foo(a,b):
...   print do_loc(a,b)
...   print do_remte(a,b)
... 
>>> with mock.patch('__main__.do_remte') as mock_remte:
...   mock_remte.return_value = "MY MOCK"
...   foo('A', 'B')
... 
IN do_loc A B
MY MOCK
>>> 
1
Vor 29 ago. 2014 a las 14:52

Esta es casi la misma respuesta que Martijn Pieters, pero usando la biblioteca mock.

import module_under_test
try:
    # Standard library as of Python 3.4
    import unittest.mock as mock
except ImportError:
    # 3rd-party installation
    import mock

def test_fun():
    # patch remote methods with noops
    with mock.patch.multiple('module_under_test',
                             do_remotely_after_one_minute=mock.DEFAULT,
                             do_remotely_after_two_minute=mock.DEFAULT) as values:
        fun(a, b)
        # If desired, you can use values['do_remotely_after_one_minute'] to query
        # the mocked function as to whether or not it was called, what arguments
        # it was called with, etc.
1
chepner 29 ago. 2014 a las 14:52