database - pruebas - phpunit tutorial
Cómo escribir pruebas unitarias para llamadas a bases de datos (9)
Estoy cerca del comienzo de un nuevo proyecto y (¡jadeó!) Por primera vez estoy tratando de incluir pruebas unitarias en un proyecto mío.
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 esencialmente toma todos los bits y piezas necesarios 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é debería incluso probar en una situación como esta?
Una vez resuelto esto, surge la cuestión de si se deben burlar los componentes de la base de datos o intentar probarlos con la base de datos real.
¿Qué estás probando?
Hay tres posibilidades, fuera de mi cabeza:
A. Usted está probando la clase DAO (objeto de acceso a datos), asegurándose de que está ordenando correctamente los valores / parámetros que se pasan a la base de datos, y los resultados de clasificación / transformación / empaquetado correctos 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ás probando la corrección sintáctica del SQL (generado).
En este caso, debido a que los dialectos de SQL son diferentes, usted quiere ejecutar el SQL (posiblemente generado) contra la versión correcta de su RDBMS, en lugar de intentar simular todas las peculiaridades de su RDBMS (y para que cualquier mejora de RDBMS que cambie la funcionalidad sea atrapada por tus pruebas).
C. Está probando la corrección semántica de su SQL, es decir, que para un conjunto de datos de referencia dado, sus operaciones (accesos / selecciones y mutaciones / inserciones y actualizaciones) producen el nuevo conjunto de datos esperado.
Para eso, quieres usar algo como dbunit (que te permite configurar una línea base y comparar un conjunto de resultados con un conjunto de resultados esperado), o posiblemente hacer tus pruebas completamente en la base de datos, usando la técnica que describo aquí: La mejor manera para probar las consultas SQL .
El objetivo de una prueba unitaria es probar una unidad (duh) aisladamente. El objetivo de una llamada a la base de datos es integrarse con otra unidad (la base de datos). Ergo: no tiene sentido para las llamadas a la base de datos de prueba unitaria.
Sin embargo, debe realizar llamadas a la base de datos de prueba de integración (y puede usar las mismas herramientas que usa para las pruebas de unidades, si lo desea).
En un proyecto basado en JDBC, la conexión JDBC puede ser burlada, 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 las consultas / parámetros correctos (ej. https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/ParameterSpec.scala ) y maneja Resultados de JDBC (análisis / mapeo) como se esperaba ("toma todos los bits y piezas necesarios para extraer algunos datos de la base de datos y devuelve los datos en un objeto DataTable").
El marco como jOOQ o Acolyte se puede utilizar para: https://github.com/cchantep/acolyte .
Es por eso que 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, repleta de montones de datos basura, y ejecutar las pruebas de componentes de la 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.
Estrictamente hablando, una prueba que escribe / lee desde una base de datos o un sistema de archivos no es una prueba unitaria. (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 "Testing w / DB" del libro. Las claves son:
- utilizar pruebas de ida y vuelta
- no escriba demasiadas pruebas en su dispositivo de prueba de acceso a datos, ya que funcionarán mucho más lento que sus pruebas de unidades "reales"
- si puede evitar las pruebas con una base de datos real, pruebe sin una base de datos
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í hay un enlace de este sitio sobre este tema, pero hay otros: ¿ Dependency Injection en .NET con ejemplos?
Esto le permitiría burlarse más fácilmente de otras partes de su aplicación, simplemente haciendo que una clase simulada implemente la interfaz, para que pueda controlar cómo responderá. Pero, esto también significa diseñar una interfaz.
Ya que preguntaste sobre las mejores prácticas, esta sería una, OMI.
Entonces, 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 bases de datos para eso, pero generalmente no es mejor ir a una base de datos real, especialmente porque más de una persona puede ejecutar una prueba unitaria en una 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
Para las 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 esté utilizando la conexión de los parámetros, que la cadena de consulta esté asignada al comando, y que su conjunto de resultados devuelto sea el mismo que el que proporciona 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.
Por el amor de Dios, no pruebes en una base de datos en vivo y ya poblada. Pero tú lo sabías
En general, ya tiene una idea de qué tipo de datos va a recuperar cada consulta, ya sea que esté autenticando usuarios, buscando entradas de agenda / organigrama, o lo que sea. Usted sabe en qué campos está interesado 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 maneje correctamente los valores NULL
. Si uno de tus campos es una cadena ( CHAR
, VARCHAR
, TEXT
, & c), prueba para asegurarte de que estás manejando los caracteres escapados correctamente.
Suponga 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.
Puede probarlo 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, no veo las pruebas de integración como 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 yo estuviera probando este código por unidades, estaría validando que los parámetros se compilan correctamente, ¿crea el constructor de comandos la cantidad correcta de parámetros? ¿Todos ellos tienen un valor? ¿Los nulos, cadenas vacías y DbNull se manejan correctamente?
En realidad, completar el conjunto de datos es probar su base de datos, que es un componente descabellado fuera del alcance de su DAL.