49

Estoy cerca del comienzo de un nuevo proyecto y (¡jadeó!) Por primera vez estoy intentando incluir pruebas unitarias en un proyecto mío.Cómo escribir pruebas unitarias para llamadas a bases de datos

Tengo problemas para diseñar algunas de las pruebas unitarias. Tengo algunos métodos que han sido fáciles de probar (pasar dos valores y verificar el resultado esperado). Tengo otras partes del código que hacen cosas más complejas como ejecutar consultas en la base de datos y no estoy seguro de cómo probarlas.

public DataTable ExecuteQuery(SqlConnection ActiveConnection, string Query, SqlParameterCollection Parameters) 
{ 
    DataTable resultSet = new DataTable(); 
    SqlCommand queryCommand = new SqlCommand(); 
    try 
    { 
     queryCommand.Connection = ActiveConnection; 
     queryCommand.CommandText = Query; 

     if (Parameters != null) 
     { 
      foreach (SqlParameter param in Parameters) 
      { 
       queryCommand.Parameters.Add(param); 
      } 
     } 

     SqlDataAdapter queryDA = new SqlDataAdapter(queryCommand); 
     queryDA.Fill(resultSet); 
    } 
    catch (Exception ex) 
    { 
     //TODO: Improve error handling 
     Console.WriteLine(ex.Message); 
    } 

    return resultSet; 
} 

Este método toma esencialmente en todas las partes y piezas necesarias para extraer algunos datos de la base de datos, y devuelve los datos en un objeto DataTable.

La primera pregunta es probablemente la más compleja: ¿Qué debo incluso de prueba en una situación como esta?

Una vez que eso está arreglado viene la pregunta de si o no para burlarse de los componentes de base de datos o tratar de probar contra la base de datos real.

Respuesta

37

¿Qué estás probas?

Hay tres posibilidades, la parte superior de mi cabeza:

A. Usted está probando la DAO (acceso a objetos de datos) de clase, asegurándose de cálculo de referencias correctamente los valores/parámetros que se pasan a la la base de datos y los resultados de clasificación/transformación/embalaje correctamente obtenidos de la base de datos.

En este caso, no necesita conectarse a la base de datos; solo necesita una prueba unitaria que reemplace la base de datos (o capa intermedia, p. ej., JDBC, (N) Hibernate, iBatis) con un simulacro.

B. Está probando la corrección sintáctica del SQL (generado).

En este caso, como dialectos SQL difieren, que desea ejecutar el SQL (posiblemente generada) en contra de la versión correcta de su RDBMS, en lugar de intentar burlarse de todos los caprichos de su RDBMS (y por lo que cualquier mejora de RDBMS que las pruebas cambian la funcionalidad del cambio).

C. Usted está probando la corrección semántica de su SQL, es decir, que para un conjunto de datos de referencia dado, sus operaciones (accesos/selecciona y mutaciones/inserciones y actualizaciones) producir el nuevo conjunto de datos esperado.

Para ello, se quieren usar algo como DBUnit (que le permite establecer una línea de base y compara un conjunto de resultados a un conjunto de resultados esperados), o posiblemente hacer su prueba en su totalidad en la base de datos, utilizando la técnica Describo aquí: Best way to test SQL queries.

1

Para pruebas unitarias, generalmente me burlo o simulo la base de datos. Luego use su implementación falsa o falsa a través de la inyección de dependencia para probar su método. Probablemente también tenga algunas pruebas de integración que pondrán a prueba restricciones, relaciones de claves externas, etc. en su base de datos.

En cuanto a lo que probaría, se aseguraría de que el método utiliza la conexión de los parámetros, que la cadena de consulta se asigna al comando, y que el conjunto de resultados devuelto es el mismo que el que está proporcionando a través de una expectativa en el método de relleno. Nota: probablemente sea más fácil probar un método Get que devuelve un valor que un método Fill. Modifica un parámetro.

5

Por el amor de Dios, no lo pruebes en una base de datos en vivo ya poblada. Pero tú lo sabías

En general ¿Ya tiene una idea de qué tipo de datos de cada consulta se va a recuperar, ya sea que la autenticación de usuarios, mirando hacia arriba de la agenda/entradas del organigrama, o lo que sea. Sabe qué campos le interesan y sabe qué restricciones existen en ellos (por ejemplo, UNIQUE, NOT NULL, etc.). Está probando de forma unitaria su código que interactúa con la base de datos, no la base de datos en sí, así que piense en términos de cómo probar esas funciones. Si es posible que un campo sea NULL, debe hacerse una prueba que asegure que su código maneja los valores NULL correctamente. Si uno de sus campos es una cadena (CHAR, VARCHAR, TEXT, & c), prueba para asegurarse de que está manejando correctamente caracteres de escape.

Supongamos que los usuarios intentarán poner cualquier cosa * en la base de datos y generar casos de prueba en consecuencia. Querrá usar objetos simulados para esto.

* Incluye entradas no deseadas, maliciosas o no válidas.

+1

En realidad, aparece un punto interesante. ¿Hay herramientas que ayuden a crear pruebas unitarias explícitamente para la capa de la base de datos? (En otras palabras, la unidad prueba los propios procs?) –

+1

Ah, parece que mi pregunta ha sido formulada y respondida, aquí: http://stackoverflow.com/questions/754527/best-way-to-test-sql- consultas/754570 # 754570 –

2

En sentido estricto, una prueba que escribe/lee desde una base de datos o un sistema de archivos no es una prueba de unidad. (Aunque puede ser una prueba de integración y puede escribirse usando NUnit o JUnit). Se supone que las pruebas unitarias prueban las operaciones de una sola clase, aislando sus dependencias. Por lo tanto, cuando escribe pruebas unitarias para las capas de interfaz y de lógica de negocios, no necesita una base de datos en absoluto.

Bien, pero ¿cómo prueba la capa de acceso a la base de datos? Me gusta el consejo de este libro: xUnit Test Patterns (el enlace apunta al capítulo "Prueba con DB" del libro.Las claves son:

  • uso de pruebas de ida y vuelta
  • no escriben demasiadas pruebas en el dispositivo de prueba de acceso a datos, porque van a funcionar mucho más lento que su unidad "real" pruebas
  • si puede evitar las pruebas con una base de datos real, pruebe sin una base de datos
1

Para hacer esto correctamente, debería usar alguna inyección de dependencia (DI), y para .NET hay varias. Actualmente estoy usando Unity Framework, pero hay otros que son más fáciles.

Aquí es un enlace desde este sitio sobre este tema, pero hay otros: Dependency Injection in .NET with examples?

Esto permitiría a burlarse más fácilmente a cabo otras partes de su aplicación, con sólo tener una clase simulada implementa la interfaz , para que pueda controlar cómo responderá. Pero, esto también significa diseñar una interfaz.

Como ha preguntado acerca de las mejores prácticas, esta sería una, OMI.

Luego, no ir a la base de datos a menos que sea necesario, como se sugiere es otra.

Si necesita probar ciertos comportamientos, como relaciones de claves externas con eliminación en cascada, puede escribir pruebas de base de datos para eso, pero generalmente no es mejor ir a una base de datos real, especialmente porque se puede ejecutar más de una persona una prueba unitaria a la vez y si van a la misma base de datos, las pruebas pueden fallar ya que los datos esperados pueden cambiar.

Editar: Por prueba de unidad de base de datos me refiero a esto, ya que está diseñado para usar simplemente t-sql para hacer algunas configuraciones, pruebas y desmontaje. http://msdn.microsoft.com/en-us/library/aa833233%28VS.80%29.aspx

+0

Pero en este caso, DESEA que sus pruebas fallen cuando encuentren datos inesperados, de modo que pueda volver a escribir su componente para manejar la condición correctamente. – MusiGenesis

+0

Creo que usar la prueba de base de datos que agregué sería una referencia, ya que ayuda a limitar lo que debe hacer para prepararse para una prueba, por lo que no tiene que probar la configuración de una conexión, por ejemplo. –

19

Por esta razón, las pruebas unitarias (en mi humilde opinión) a veces pueden crear una falsa sensación de seguridad por parte de los desarrolladores. En mi experiencia con aplicaciones que hablan con una base de datos, los errores suelen ser el resultado de que los datos se encuentran en un estado inesperado (valores inusuales o faltantes, etc.). Si rutinariamente simula el acceso a datos en las pruebas de su unidad, pensará que su código está funcionando bien cuando de hecho todavía es vulnerable a este tipo de error.

Creo que su mejor enfoque es tener una base de datos de prueba a mano, llena de datos basura, y ejecutar sus pruebas de componentes de base de datos en contra de eso. Mientras tanto, recuerde que sus usuarios serán mucho mejores que usted en el aturdimiento de sus datos.

3

Puede probar la unidad de todo excepto: queryDA.Fill (resultSet);

Tan pronto como ejecute queryDA.Fill (resultSet), o tiene que simular/falsificar la base de datos, o está haciendo pruebas de integración.

Por mi parte, las pruebas de integración no son malas, es solo que detectará un tipo diferente de error, tiene diferentes probabilidades de falsos negativos y falsos positivos, no es probable que se haga con mucha frecuencia porque es muy lento

Si estaba probando un equipo con este código, estaría validando que los parámetros se compilan correctamente, ¿crea el constructor de comandos el número correcto de parámetros? ¿Todos ellos tienen un valor? ¿Los nulos, cadenas vacías y DbNull se manejan correctamente?

En realidad, llenar el conjunto de datos está probando su base de datos, que es un componente descabellado fuera del alcance de su DAL.

7

El objetivo de una prueba de unidad es probar una unidad (duh) de forma aislada. El objetivo de una llamada a la base de datos es integrar con otra unidad (la base de datos). Ergo: no tiene sentido para las llamadas a la base de datos de prueba unitaria.

Debería, sin embargo, realizar llamadas a la base de datos de prueba de integración (y puede usar las mismas herramientas que usa para probar la unidad si lo desea).

0

En un proyecto basado en JDBC, la conexión JDBC se puede burlar, de modo que las pruebas se pueden ejecutar sin RDBMS en vivo, con cada caso de prueba aislado (sin conflicto de datos).

Permite verificar, el código de persistencia pasa consultas/parámetros correctos (por ejemplo, https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/ParameterSpec.scala) y maneja resultados JDBC (análisis/mapeo) como se esperaba ("toma todos los bits necesarios para extraer algunos datos de la base de datos, y devuelve los datos en un objeto DataTable ").

Marco como jOOQ o Acolyte se puede utilizar para: https://github.com/cchantep/acolyte.

Cuestiones relacionadas