¿El hecho de tener un interruptor para activar o desactivar el simulacro de un código huele?

7

Tengo un método que se ve así:

def foobar(mock=False, **kwargs):
    # ... snipped

foobar en realidad hace varias llamadas a Amazon S3 y devuelve un resultado compuesto. Para hacer esto verificable, introduje el parámetro mock para desactivar hacer conexiones de red en vivo. Se siente como un olor a código para mí, pero la capacidad de prueba también es muy importante. ¿Qué más puedo hacer si quiero eliminar el parámetro?

    
pregunta Bon Ami 01.08.2011 - 04:13

3 respuestas

29

¿Por qué no tener una clase de conexión?

class Connection(object):
    def retrieve(self, resource):
        return something_from_s3()

class MockConnection(Connection):
    def retrieve(self, resource):
        return 42

def foobar(connection = Connection(), **kwargs):
    whatever = connection.retrieve("foobar")

Esto no solo es más limpio, sino que también puedes probarlo con varias conexiones simuladas. Si alguna vez decide admitir un tipo de servicio diferente en lugar de S3, puede ampliar fácilmente su producto para admitirlo.

    
respondido por el carl 01.08.2011 - 04:20
10

Sí, eso es un enorme olor de código ENORME.

Si quieres probar algo que está respaldado por S3, apaga o simula las llamadas que hagas a tu API S3 y hazlo de esa manera.

Para ver un ejemplo simple, echa un vistazo a Paperclip: enlace

    
respondido por el Dan Cheail 01.08.2011 - 04:18
0

A veces, es más fácil pasar un poco de "motor s3" como parámetro y pasar un simple simulacro : en lugar de un objeto prueba (y proporcionar sus valores de retorno). A veces es necesario llamar muchas funciones aleatorias que causan efectos secundarios (por ejemplo, si trabajaría con s3, obtendría la hora actual, haría algunas consultas de SQL, abrirá algunos archivos y más) y aquí no sería agradable. para pasarlo todo como parámetros de función (ya que no es necesario, excepto para la capacidad de prueba), le sugiero que utilice mockstar para Describa declarativamente sus efectos secundarios. Tu código entonces sería algo como esto:

def foobar():
    foo = get_from_database()
    bar = read_from_file()
    baz = read_from_s3()

    if foo > 10:
        return foo + bar + baz
    else:
        return foo - bar - baz

# And your test would look something like this:

from mockstar import prefixed_p
from nose.tools import assert_equal

ppatch = prefixed_p('module.with.foobar.func')


class TestFoobar(BaseTestCase):
    @ppatch('get_from_database')
    @ppatch('read_from_file')
    @ppatch('read_from_s3')
    def side_effects(self, se):
        se.read_from_file.return_value = 10  # default behaviour
        se.read_from_s3.return_value = 0  # also default behaviour
        return self.invoke(se)

    def test_should_get_30(self, se):
        se.get_from_database.return_value = 20

        # do
        result = foobar()

        assert_equal(result, 30)

    def test_should_get_minus5(self, se):
        se.get_from_database.return_value = 5

        # do
        result = foobar()

        assert_equal(result, -5)
    
respondido por el Kostiantyn Rybnikov 31.01.2013 - 17:11

Lea otras preguntas en las etiquetas