2011-04-15 6 views
7

El proyecto en el que estoy trabajando es un software de lógica de negocios envuelto como un paquete de Python. La idea es que varias secuencias de comandos o aplicaciones lo importarán, lo inicializarán y luego lo usarán.¿Cómo lograr correctamente el aislamiento de prueba con un módulo de Python con estado?

Actualmente tiene un método init() de nivel superior que realiza la inicialización y configura varias cosas, un buen ejemplo es que configura SQLAlchemy con una conexión db y almacena la sesión SA para acceso posterior. Se está almacenando en un subpaquete de mi proyecto (es decir, myproj.model.Session, para que otro código pueda obtener una sesión de SA en funcionamiento después de importar el modelo).

En resumen, esto hace que mi paquete sea completo. Estoy escribiendo pruebas unitarias para el proyecto y este comportamiento stafeful plantea algunos problemas:

  • pruebas deben ser aislados, pero el estado interno de mi paquete rompe este aislamiento
  • no puedo probar el init principal método

      () ya que su comportamiento depende del estado necesitarán
    1. pruebas futuras que se ejecute contra la (todavía no escrito) parte del controlador con un estado modelo bien conocido (por ejemplo. un pre-poblado sqlitein-memory db)

    ¿Debo de alguna manera refactorizar mi packag e porque la estructura actual no es la mejor (posible) práctica (tm)? :)

    ¿Debo dejarlo así y configurar/desmontar todo el asunto todo el tiempo? Si voy a lograr el aislamiento completo que significaría borrar completamente y volver a poblar el DB en cada prueba, ¿no es esa exageración?

    Esta pregunta está realmente en el código general & prueba la estructura, pero para lo que vale estoy usando nose-1.0 para mis pruebas. Sé que el Isolate plugin probablemente podría ayudarme, pero me gustaría obtener el código correcto antes de hacer cosas extrañas en el banco de pruebas.

  • Respuesta

    2

    Usted tiene algunas opciones:

    Mock la base de datos

    Hay algunas compensaciones a tener en cuenta.

    Sus pruebas se volverán más complejas ya que tendrá que hacer la configuración, desmontaje y burla de la conexión. Es posible que también desee verificar los SQL/comandos enviados. También tiende a crear un tipo extraño de acoplamiento ajustado que puede hacer que dedique tiempo adicional a mantener/actualizar las pruebas cuando el esquema o SQL cambian.

    Esto es generalmente el más puro para el aislamiento de prueba porque reduce una dependencia potencialmente grande de las pruebas. También tiende a hacer que las pruebas sean más rápidas y reduce los gastos generales para automatizar el conjunto de pruebas en, por ejemplo, un entorno de integración continua.

    recrear la base de datos con cada prueba

    compensaciones comerciales a tener en cuenta.

    Esto puede hacer que su prueba sea muy lenta, dependiendo de cuánto tiempo tarde realmente en volver a crear su base de datos. Si el servidor de la base de datos dev es un recurso compartido, tendrá que haber una inversión inicial adicional para asegurarse de que cada desarrollador tenga su propia base de datos en el servidor. El servidor puede verse afectado según la frecuencia con la que se ejecutan las pruebas. Hay una sobrecarga adicional para ejecutar su conjunto de pruebas en un entorno de integración continua porque necesitará al menos, posiblemente más db (dependiendo de cuántas ramas se están creando simultáneamente).

    El beneficio tiene que ver con ejecutar realmente las mismas rutas de código y recursos similares que se utilizarán en la producción. Esto generalmente ayuda a revelar errores más temprano, lo que siempre es algo muy bueno.

    de intercambio ORM DB

    Si su uso de un ORM como SQLAlchemy su es una posibilidad que se puede intercambiar la base de datos subyacente con una base de datos potencialmente más rápido en memoria. Esto le permite mitigar algunos de los aspectos negativos de ambas opciones anteriores.

    No es exactamente la misma base de datos que se utilizará en producción, pero el ORM debería ayudar a mitigar el riesgo que oculta un error. Normalmente, el tiempo para configurar una base de datos en memoria es mucho más corto que el que está respaldado por archivos. También tiene la ventaja de estar aislado de la ejecución de prueba actual, por lo que no tiene que preocuparse por la gestión de recursos compartidos o la eliminación/limpieza final.

    1

    Trabajando en un proyecto con una configuración relativamente costosa (IPython), he visto un enfoque utilizado donde llamamos a una función get_ipython, que configura y devuelve una instancia, mientras se reemplaza con una función que devuelve una referencia a la instancia existente. Entonces, cada prueba puede llamar a la misma función, pero solo hace la configuración para la primera.

    Eso ahorra hacer un largo procedimiento de configuración para cada prueba, pero ocasionalmente crea casos extraños donde una prueba falla o pasa dependiendo de las pruebas que se realizaron antes. Tenemos formas de lidiar con eso: muchas de las pruebas deberían hacer lo mismo, independientemente del estado, y podemos intentar restablecer el estado del objeto antes de ciertas pruebas. Es posible que encuentre una compensación similar para usted.

    0

    Mock es una herramienta simple y potente para lograr cierto aislamiento. Hay un buen video de Pycon2011 que muestra cómo usarlo. Recomiendo usarlo junto con py.test, que reduce la cantidad de código requerido para definir las pruebas y sigue siendo muy, muy poderoso.

    Cuestiones relacionadas