TDD con patrón de repositorio

10

En mi nuevo proyecto, decidí probar con TDD. Y al principio me encontré con un problema. Lo primero que quiero hacer en mi aplicación es dar la capacidad de leer datos de la fuente de datos. Para este propósito, quiero usar el patrón de repositorio. Y ahora:

  • Si la prueba es para la implementación real de la interfaz del repositorio, probaré la clase que tiene acceso a la base de datos, y sé que debería evitar eso.
  • Si la prueba no es para la implementación real del patrón de repositorio, lo estaré probando bien ... solo simulacro. No habrá ninguna pieza de código de producción probada en esas pruebas unitarias.

Estoy pensando en esto desde hace dos días y todavía no puedo encontrar ninguna solución razonable. ¿Qué debería hacer?

Thaven
fuente

Respuestas:

11

Lo que hace un repositorio es traducir de su dominio a su marco DAL, como NHibernate o Doctrine, o sus clases de ejecución de SQL. Esto significa que su repositorio llamará a métodos en dicho marco para realizar sus tareas: su repositorio construye las consultas necesarias para obtener los datos. Si no está utilizando un marco ORM (espero que sea ...), el repositorio sería el lugar donde se construyen las sentencias SQL sin procesar.

El más básico de estos métodos es guardar: en la mayoría de los casos, esto simplemente pasará el objeto del repositorio a la unidad de trabajo (o la sesión).

public void Save(Car car)
{
    session.Save(car);
}

Pero veamos otro ejemplo, por ejemplo, buscar un automóvil por su ID. Puede parecer

public function GetCarWithId(String id)
{
    return Session.QueryOver<Car>()
                    .Where(x => x.Id == id)
                    .SingleOrDefault();
}

Todavía no es demasiado complejo, pero se puede imaginar con múltiples condiciones (consígame todos los autos fabricados después de 2010 para todas las marcas en el grupo 'Volkswagen') esto se vuelve complicado. Entonces, al verdadero estilo TDD, debe probar esto. Hay varias maneras de hacer esto.

Opción 1: burlarse de las llamadas realizadas al marco ORM

Claro, puede burlarse del objeto Session y simplemente afirmar que se realizan las llamadas correctas. Si bien esta prueba el repositorio, en realidad no es de Exámenes impulsado porque se está haciendo una prueba de que el repositorio interno tenga el aspecto que desea. La prueba básicamente dice 'el código debería verse así'. Aún así, es un enfoque válido, pero parece que este tipo de prueba tiene muy poco valor.

Opción 2: (Re) construir la base de datos a partir de las pruebas

Algunos marcos DAL le brindan la capacidad de construir la estructura completa de la base de datos basada en los archivos de mapeo que crea para mapear el dominio en las tablas. Para estos marcos, la forma de probar repositorios es a menudo crear la base de datos con una base de datos en memoria en el primer paso de la prueba y agregar objetos usando el marco DAL a la base de datos en memoria. Después de esto, puede usar el repositorio en la base de datos en memoria para probar si los métodos funcionan. Estas pruebas son más lentas, pero muy válidas y conducen sus pruebas. Requiere cierta cooperación de su marco DAL.

Opción 3: prueba en una base de datos real

Otro enfoque es probar en una base de datos real y aislar la prueba unitaria. Puede hacer esto de varias maneras: rodee sus pruebas con una transacción, limpie manualmente (no recomendaría que sea muy difícil de mantener), reconstruya completamente la base de datos después de cada paso ... Dependiendo de la aplicación que esté creando, esto puede o no No será factible. En mis aplicaciones puedo construir completamente una base de datos de desarrollo local a partir del control de código fuente y mis pruebas unitarias en repositorios usan transacciones para aislar completamente las pruebas entre sí (transacción abierta, insertar datos, repositorio de prueba, transacción de reversión). Cada compilación primero configura la base de datos de desarrollo local y luego realiza pruebas unitarias aisladas de transacción para los repositorios en esa base de datos de desarrollo local. Eso'

No pruebes el DAL

Si está utilizando un marco DAL como NHibernate, evite la necesidad de probar ese marco. Puede probar sus archivos de mapeo guardando, recuperando y luego comparando un objeto de dominio para asegurarse de que todo esté bien (asegúrese de deshabilitar cualquier tipo de almacenamiento en caché), pero no es tan necesario como muchas otras pruebas que debería escribir. Tiendo a hacer esto principalmente para colecciones de padres con condiciones en los niños.

Al probar la devolución de sus repositorios, simplemente puede verificar si alguna propiedad de identificación en su objeto de dominio coincide. Esto puede ser una identificación, pero en las pruebas a menudo es más beneficioso verificar una propiedad legible por humanos. En la sección 'Consígueme todos los autos fabricados después de 2010 ...', esto podría simplemente verificar que se devuelvan cinco autos y que las placas de matrícula sean 'insertar lista aquí'. El beneficio adicional es que te obliga a pensar en la clasificación Y tu prueba automáticamente fuerza la clasificación. Se sorprendería de la cantidad de aplicaciones que se ordenan varias veces (se devuelven ordenadas de la base de datos, se ordenan antes de crear un objeto de vista y luego se ordenan todas, en la misma propiedad por si acaso ) o asumen implícitamente el tipo de repositorio y se eliminan accidentalmente que en algún momento del camino rompieron la interfaz de usuario.

'Prueba unitaria' es solo un nombre

En mi opinión, las pruebas unitarias deben sobre todo no golpear la base de datos. Construye una aplicación para que cada parte del código que necesita datos de una fuente haga esto con un repositorio, y ese repositorio se inyecta como una dependencia. Esto permite una burla fácil y toda la bondad TDD que desee. Pero al final desea asegurarse de que sus repositorios cumplan con sus obligaciones y, si la forma más fácil de hacerlo es acceder a una base de datos, bueno, que así sea. Hace tiempo que dejé de lado la noción de que 'las pruebas unitarias no deberían tocar la base de datos' y aprendí que hay razones muy reales para hacerlo. Pero solo si puede hacerlo de forma automática y repetida. Y el clima que llamamos tal prueba una 'prueba de unidad' o una 'prueba de integración' es discutible.

JDT
fuente
3
Las pruebas unitarias y las pruebas de integración tienen diferentes propósitos. Los nombres para estas pruebas no son meramente decorativos; También son descriptivos.
Robert Harvey
9
  1. No pruebe métodos de repositorio triviales u obvios.

    Si los métodos son operaciones CRUD triviales, todo lo que realmente está probando es si los parámetros se asignan correctamente. Si tiene pruebas de integración, tales errores serán evidentes de todos modos de inmediato.

    Este es el mismo principio que se aplica a las propiedades triviales, como este:

    public property SomeProperty
    {
        get { return _someProperty; }
        set { _someProperty = value; }
    }
    

    No lo pruebas, porque no hay nada que probar. No hay validación u otra lógica en la propiedad que deba verificarse.

  2. Si aún quieres probar esos métodos ...

    Los simulacros son la forma de hacerlo. Recuerde, estas son pruebas unitarias. No prueba la base de datos con pruebas unitarias; para eso son las pruebas de integración.

Más información
The Full Stack, Parte 3: Creación de un repositorio utilizando TDD (comience a mirar aproximadamente a los 16 minutos).

Robert Harvey
fuente
3
Claro, entiendo esto. Aún así, si se trata de un enfoque TDD, no debería escribir ningún código si no tengo pruebas para este código primero, ¿verdad?
Thaven
1
@Thaven: hay una serie de videos en YouTube titulada "¿Está muerto?". Miralos. Abordan muchos puntos interesantes, uno de los cuales es la idea de que aplicar TDD en todos los niveles de su aplicación no es necesariamente la mejor idea. "ningún código sin una prueba fallida" es una posición demasiado extrema, es una de las conclusiones.
Jules
2
@Jules: ¿Cuál es el tl; dw?
Robert Harvey
1
@RobertHarvey Es difícil de resumir, pero el punto más importante fue que tratar el TDD como una religión que siempre se debe observar es un error. La opción de usarlo es parte de una compensación y debe tener en cuenta que (1) puede trabajar más rápido sin problemas y (2) puede empujarlo hacia una solución más compleja de la que necesita, particularmente si te encuentras usando muchas burlas.
Jules
1
+1 para el punto 1. Las pruebas pueden estar equivocadas, es solo que generalmente son triviales. No tiene sentido probar una función cuya corrección es más obvia que la de la prueba. No es como obtener una cobertura de código del 100% que te acerque a probar todas las ejecuciones posibles del programa, por lo que podrías ser inteligente sobre dónde gastas el esfuerzo de prueba.
Doval