¿Cómo simular una base de datos para pruebas (Java)?

76

Estoy programando en Java y mis aplicaciones utilizan mucho DB. Por lo tanto, es importante para mí poder probar el uso de mi base de datos fácilmente.
¿De qué se tratan las pruebas de base de datos? Para mí, deberían proporcionar dos requisitos simples:

  1. Verifique la sintaxis SQL.
  2. Más importante aún, verifique que los datos estén seleccionados / actualizados / insertados correctamente, de acuerdo con una situación dada.

Bueno, parece que todo lo que necesito es una base de datos.
Pero en realidad, prefiero no, ya que hay pocas dificultades para usar una base de datos para una prueba:

  • "Consiga una base de datos de prueba, ¿qué tan difícil podría ser?" - Bueno, en mi lugar de trabajo, tener un DB de prueba personal es bastante imposible. Tienes que utilizar una base de datos "pública", que sea accesible para todos.
  • "Estas pruebas no son rápidas ..." - Las pruebas de DB tienden a ser más lentas que las pruebas habituales. Realmente no es ideal tener pruebas lentas.
  • "¡Este programa debería manejar cualquier caso!" - Se vuelve algo molesto e incluso imposible intentar simular todos y cada uno de los casos en una base de datos. Para cada caso, se debe realizar una cierta cantidad de consultas de inserción / actualización, lo cual es molesto y lleva tiempo.
  • "Espera un segundo, ¿cómo sabes que hay 542 filas en esa tabla?" - Uno de los principios fundamentales en las pruebas es poder probar la funcionalidad de una manera diferente a la de su código probado. Cuando se usa una base de datos, generalmente hay una forma de hacer algo, por lo tanto, la prueba es exactamente la misma que el código central.

Entonces, puede darse cuenta de que no me gustan las bases de datos cuando se trata de pruebas (por supuesto, tendré que llegar a esto en algún momento, pero prefiero llegar más tarde en mis pruebas, después de encontrar la mayoría de los errores usando el resto de los métodos de prueba). ¿Pero qué estoy buscando?

Estoy buscando una forma de simular una base de datos, una base de datos simulada, usando el sistema de archivos o simplemente la memoria virtual. Pensé que tal vez hay una herramienta / paquete Java que permite simplemente construir (usando la interfaz de código) un simulacro de base de datos por prueba, con tablas y filas simuladas, con verificación de SQL y con una interfaz de código para monitorear su estado (en lugar de usar SQL ).

¿Está familiarizado con este tipo de herramienta?


Editar: ¡ Gracias por las respuestas! Aunque estaba pidiendo una herramienta, también me brindó algunos consejos sobre el problema :) Me llevará algún tiempo revisar sus ofertas, así que no puedo decir ahora si sus respuestas fueron satisfactorias o no.

De todos modos, aquí hay una mejor vista de lo que estoy buscando: imagina una clase llamada DBMonitor, que una de sus características es encontrar el número de filas en una tabla. Aquí hay un código imaginario de cómo me gustaría probar esa característica usando JUnit:

public class TestDBMonitor extends TestCase {

    @Override
    public void setUp() throws Exception {

       MockConnection connection = new MockConnection();

       this.tableName = "table1";
       MockTable table = new MockTable(tableName);

       String columnName = "column1";
       ColumnType columnType = ColumnType.NUMBER;
       int columnSize = 50;
       MockColumn column = new MockColumn(columnName, columnType, columnSize);
       table.addColumn(column);

       for (int i = 0; i < 20; i++) {
           HashMap<MockColumn, Object> fields = new HashMap<MockColumn, Object>();
           fields.put(column, i);
           table.addRow(fields);
       }

       this.connection = connection;
    }

    @Test
    public void testGatherStatistics() throws Exception {

       DBMonitor monitor = new DBMonitor(connection);
       monitor.gatherStatistics();
       assertEquals(((MockConnection) connection).getNumberOfRows(tableName),
                    monitor.getNumberOfRows(tableName));
    }

    String tableName;
    Connection connection;
}

Espero que este código sea lo suficientemente claro para entender mi idea (disculpe los errores de sintaxis, estaba escribiendo manualmente sin mi querido Eclipse: P).

Por cierto, utilizo ORM parcialmente y mis consultas SQL sin procesar son bastante simples y no deberían diferir de una plataforma a otra.

Eyal Roth
fuente

Respuestas:

24

nueva respuesta a la pregunta anterior (pero las cosas han avanzado un poco):

¿Cómo simular una base de datos para pruebas (Java)?

no lo simulas. usted se burla de sus repositorios y no los prueba o usa la misma base de datos en sus pruebas y prueba sus sqls. Todas las bases de datos en memoria no son totalmente compatibles, por lo que no le brindarán cobertura ni confiabilidad completas. y nunca intente simular / simular los objetos de la base de datos profunda como la conexión, el conjunto de resultados, etc., no le da ningún valor y es una pesadilla para desarrollar y mantener

tener una base de datos de prueba personal es bastante imposible. Tienes que usar una base de datos "pública", que sea accesible para todos.

Desafortunadamente, muchas empresas todavía usan ese modelo, pero ahora tenemos Docker y hay imágenes para casi todos los DB. Los productos comerciales tienen algunas limitaciones (como hasta unos pocos GB de datos) que no son importantes para las pruebas. también necesita que su esquema y estructura se creen en esta base de datos local

"Estas pruebas no son rápidas ..." - Las pruebas de DB tienden a ser más lentas que las pruebas habituales. Realmente no es ideal tener pruebas lentas.

sí, las pruebas de db son más lentas, pero no tanto. Hice algunas mediciones simples y una prueba típica tomó de 5 a 50 ms. lo que lleva tiempo es el inicio de la aplicación. hay muchas formas de acelerar esto:

  • Los primeros marcos de DI (como Spring) ofrecen una forma de ejecutar solo una parte de su aplicación. si escribe su aplicación con una buena separación de lógica relacionada con db y no db, entonces en su prueba puede comenzar solo la parte db
  • cada db tiene muchas opciones de ajuste que lo hacen menos duradero y mucho más rápido. eso es perfecto para probar. ejemplo de postgres
  • también puede poner toda la base de datos en tmpfs

  • Otra estrategia útil es tener grupos de pruebas y mantener las pruebas de db desactivadas de forma predeterminada (si realmente ralentizan su compilación). de esta manera, si alguien realmente está trabajando en db, debe pasar una bandera adicional en la línea cmd o usar IDE (los grupos de prueba y los selectores de prueba personalizados son perfectos para esto)

Para cada caso, se debe realizar una cierta cantidad de consultas de inserción / actualización, lo cual es molesto y lleva tiempo

La parte de "toma tiempo" se discutió anteriormente. es molesto He visto dos formas:

  • prepare un conjunto de datos para todos sus casos de prueba. entonces tienes que mantenerlo y razonar al respecto. generalmente está separado del código. tiene kilobytes o megabytes. es demasiado grande para verlo en una pantalla, comprender y razonar. introduce acoplamiento entre pruebas. porque cuando necesita más filas para la prueba A, sucount(*) prueba B falla. solo crece porque incluso cuando elimina algunas pruebas, no sabe qué filas fueron utilizadas solo por esta prueba
  • cada prueba prepara sus datos. de esta manera, cada prueba es completamente independiente, legible y fácil de razonar. es molesto imo, en absoluto! le permite escribir nuevas pruebas muy rápidamente y le ahorra mucho trabajo en el futuro

¿cómo sabe que hay 542 filas en esa tabla? "- Uno de los principios principales en las pruebas es poder probar la funcionalidad de una manera diferente a la de su código probado

uhm ... no realmente. el principio principal es verificar si su software genera la salida deseada en respuesta a una entrada específica. así que si llama dao.insert542 veces y luego dao.countdevuelve 542, significa que su software funciona según lo especificado. si lo desea, puede llamar a commit / drop cache en el medio. Por supuesto, a veces desea probar su implementación en lugar del contrato y luego verifica si su dao cambió el estado de la base de datos. pero siempre prueba sql A usando sql B (insertar vs seleccionar, secuencia next_val vs valor devuelto, etc.). sí, siempre tendrás el problema de 'quién probará mis pruebas', y la respuesta es: nadie, ¡así que mantenlos simples!

otras herramientas que pueden ayudarlo:

  1. testcontainers le ayudará a proporcionar una base de datos real.

  2. dbunit : lo ayudará a limpiar los datos entre pruebas

    contras:

    • se requiere mucho trabajo para crear y mantener el esquema y los datos. especialmente cuando su proyecto se encuentra en una etapa de desarrollo intensivo.
    • Es otra capa de abstracción, por lo que si de repente desea utilizar alguna función de base de datos que no es compatible con esta herramienta, puede ser difícil probarla.
  3. testegration : tiene la intención de brindarle un ciclo de vida completo, listo para usar y extensible (divulgación: soy un creador).

    contras:

    • gratis solo para proyectos pequeños
    • proyecto muy joven
  4. flyway o Liquibase - herramientas de migración db. le ayudan a crear fácilmente el esquema y todas las estructuras en su base de datos local para las pruebas.

piotrek
fuente
6
Debo reconocerlo, no pensé que alguien volvería a examinar esta pregunta y se molestaría en escribir una respuesta actualizada. Lo pregunté hace 8 años, y desde entonces adquirí experiencia, lo que me lleva a estar mayormente de acuerdo con tu respuesta, especialmente con la parte sobre "probar la funcionalidad con el mismo código".
Eyal Roth
40

Java viene con Java DB .

Dicho esto, desaconsejaría usar un tipo de base de datos diferente al que usa en producción a menos que pase por una capa ORM. De lo contrario, es posible que su SQL no sea tan multiplataforma como cree.

También echa un vistazo a DbUnit

ykaganovich
fuente
10

He usado Hypersonic para este propósito. Básicamente, es un archivo JAR (una base de datos Java pura en memoria) que puede ejecutar en su propia JVM o en su propia JVM y mientras se está ejecutando, tiene una base de datos. Luego lo detiene y su base de datos desaparece. Lo he usado, hasta ahora, como una base de datos puramente en memoria. Es muy sencillo iniciar y detener a través de Ant cuando se ejecutan pruebas unitarias.

Eddie
fuente
10

Hay muchos puntos de vista sobre cómo probar los puntos de integración, como la conexión de la base de datos a través de SQL. Mi conjunto personal de reglas que me ha funcionado bien es el siguiente:

1) Separe la lógica y las funciones de acceso a la base de datos de la lógica empresarial general y ocúltela detrás de una interfaz. Razón: Para probar la gran mayoría de la lógica en el sistema, es mejor usar un código ficticio / stub en lugar de la base de datos real, ya que es más simple. Razón 2: es dramáticamente más rápido

2) Trate las pruebas para la base de datos como pruebas de integración que están separadas del cuerpo principal de las pruebas unitarias y necesitan ejecutarse en una base de datos de configuración Razón: Velocidad y calidad de las pruebas

3) Cada desarrollador necesitará su propia base de datos distinta. Necesitarán una forma automatizada de actualizar su estructura en función de los cambios de sus compañeros de equipo e introducir datos. Ver puntos 4 y 5.

4) Utilice una herramienta como http://www.liquibase.org para administrar las actualizaciones en la estructura de su base de datos. Razón: le da agilidad en la capacidad de cambiar la estructura existente y avanzar en versiones

5) Utilice una herramienta como http://dbunit.sourceforge.net/ para administrar los datos. Configure archivos de escenario (xml o XLS) para casos de prueba particulares y datos base y solo aclare lo que se necesita para cada caso de prueba. Razón: mucho mejor que insertar y eliminar datos manualmente Razón 2: más fácil para los evaluadores entender cómo ajustar los escenarios Razón 3: es más rápido ejecutar esto

6) Necesita pruebas funcionales que también tengan DBUnit como datos de escenario, pero estos son conjuntos de datos mucho más grandes y ejecutan todo el sistema. Esto completa el paso de combinar el conocimiento de que a) Las pruebas unitarias se ejecutan y, por lo tanto, la lógica es sólida b) Que las pruebas de integración en la base de datos se ejecutan y SQL es correcto, lo que da como resultado "y el sistema en su conjunto funciona en conjunto como un pila inferior "

Esta combinación me ha servido bien hasta ahora para lograr una alta calidad en las pruebas y el producto, así como para mantener la velocidad del desarrollo de las pruebas unitarias y la agilidad para cambiar.

Paul Keeble
fuente
6

"Consiga una base de datos de prueba, ¿qué tan difícil podría ser?" - Bueno, en mi lugar de trabajo, tener un DB de prueba personal es bastante imposible. Tienes que utilizar una base de datos "pública", que sea accesible para todos.

Parece que tiene problemas culturales en el trabajo que le están proporcionando una barrera para poder hacer su trabajo al máximo de sus capacidades y el beneficio de su producto. Es posible que desee hacer algo al respecto.

Por otro lado, si el esquema de su base de datos está bajo control de versiones, siempre puede tener una compilación de prueba que cree una base de datos a partir del esquema, la llene con datos de prueba, ejecute sus pruebas, recopile los resultados y luego descarte la base de datos. Solo existirá mientras duren las pruebas. Puede ser una nueva base de datos en una instalación existente si el hardware es un problema. Esto es similar a lo que hacemos donde trabajo.

banjollity
fuente
6

Si está utilizando Oracle en el trabajo, puede utilizar la función Punto de restauración en la base de datos Flashback para que la base de datos vuelva a un momento anterior a sus pruebas. Esto eliminará cualquier cambio que haya realizado personalmente en la base de datos.

Ver:

https://docs.oracle.com/cd/E11882_01/backup.112/e10642/flashdb.htm#BRADV71000

Si necesita una base de datos de prueba para su uso con la producción / trabajo de Oracle, busque la base de datos XE, edición express de Oracle. Esto es gratuito para uso personal, con un límite de base de datos de menos de 2 GB.

Martlark
fuente
3

Recientemente cambiamos a JavaDB o Derby para implementar esto. Derby 10.5.1.1 ahora implementa una representación en memoria para que se ejecute muy rápido, no necesita ir al disco: Derby In Memory Primer

Diseñamos nuestra aplicación para que se ejecute en Oracle, PostgreSQL y Derby, de modo que no avancemos demasiado en ninguna plataforma antes de descubrir que una base de datos admite una función que otras no.

Blair Zajac
fuente
1

Estoy de acuerdo con banjollity. La configuración de entornos de prueba y desarrollo aislados debe ser una prioridad. Cada sistema de base de datos que he usado es de código abierto o tiene una edición gratuita para desarrolladores que puede instalar en su estación de trabajo local. Esto le permite desarrollar con el mismo dialecto de base de datos que la producción, le brinda acceso de administrador completo a las bases de datos de desarrollo y es más rápido que usar un servidor remoto.

Nat
fuente
1

Intenta usar derby . Es fácil y portátil. Con Hibernate, su aplicación se vuelve flexible. Prueba en el derbi, producción en cualquier cosa que te guste y confíes.

Ártico
fuente
1

Estamos creando un entorno de prueba de base de datos en funcionamiento en este momento. Creemos que debemos utilizar un sistema de gestión de base de datos real con datos simulados . Un problema con un DBMS simulado es que SQL nunca se consolidó totalmente como estándar, por lo que un entorno de prueba artificial tendría que respaldar fielmente el dialecto de nuestra base de datos de producción. Otro problema es que hacemos un uso extensivo de restricciones de valor de columna, restricciones de clave externa y restricciones únicas, y dado que una herramienta artificial probablemente no las implementaría, nuestras pruebas unitarias podrían pasar, pero las pruebas de nuestro sistema fallarían cuando llegaran por primera vez a lo real. limitaciones. Si las pruebas tardan demasiado, esto indica un error de implementación y ajustamos nuestras consultas (por lo general, los conjuntos de datos de prueba son minúsculos en comparación con la producción).

Hemos instalado un DBMS real en cada máquina de desarrollo y en nuestro servidor de prueba e integración continua (usamos Hudson). No sé cuáles son las restricciones de su política de trabajo, pero es bastante fácil de instalar y usar PostgreSQL, MySQL y Oracle XE. Todos son gratuitos para su uso en desarrollo (incluso Oracle XE), por lo que no hay ninguna razón racional para prohibir su uso.

La cuestión clave es ¿cómo garantizar que sus pruebas siempre comiencen con la base de datos en un estado coherente? Si todas las pruebas fueran de solo lectura, no hay problema. Si pudiera diseñar pruebas mutantes para que siempre se ejecuten en transacciones que nunca se confirman, no hay problema. Pero normalmente debe preocuparse por revertir las actualizaciones. Para hacer esto, puede exportar el estado inicial a un archivo, luego volver a importarlo después de la prueba (los comandos exp e imp shell de Oracle hacen esto). O puede usar un punto de control / retroceso. Pero una forma más elegante es usar una herramienta como dbunit , que nos funciona bien.

La ventaja clave de esto es que detectamos muchos más errores desde el principio, donde son mucho más fáciles de corregir y nuestras pruebas del sistema real no se bloquean mientras los desarrolladores intentan febrilmente depurar problemas. Esto significa que producimos mejor código más rápido y con menos esfuerzo.

Jim Ferrans
fuente
1

Puede utilizar HSQLDB para realizar pruebas en la base de datos de la memoria. Iniciar la base de datos en memoria y ejecutar pruebas en ella es bastante sencillo.
http://hsqldb.org/

Pratik Singhal
fuente
1

jOOQ es una herramienta que además de ofrecer abstracción SQL también tiene pequeñas herramientas integradas como un SPI que permite burlarse de la totalidad de JDBC. Esto puede funcionar de dos maneras, como se documenta en esta publicación de blog. :

Al implementar el MockDataProviderSPI:

// context contains the SQL string and bind variables, etc.
MockDataProvider provider = context -> {

    // This defines the update counts, result sets, etc.
    // depending on the context above.
    return new MockResult[] { ... }
};

En la implementación anterior, puede interceptar programáticamente cada declaración SQL y devolver un resultado para ella, incluso dinámicamente "analizando" la cadena SQL para extraer algunos predicados / información de tabla, etc.

Usando el más simple (pero menos poderoso) MockFileDatabase

... que tiene un formato como el siguiente (un conjunto de pares de declaración / resultado):

select first_name, last_name from actor;
> first_name last_name
> ---------- ---------
> GINA       DEGENERES
> WALTER     TORN     
> MARY       KEITEL   
@ rows: 3

El archivo anterior se puede leer y consumir de la siguiente manera:

import static java.lang.System.out;
import java.sql.*;
import org.jooq.tools.jdbc.*;

public class Mocking {
    public static void main(String[] args) throws Exception {
        MockDataProvider db = new MockFileDatabase(
            Mocking.class.getResourceAsStream("/mocking.txt");

        try (Connection c = new MockConnection(db));
            Statement s = c.createStatement()) {

            out.println("Actors:");
            out.println("-------");
            try (ResultSet rs = s.executeQuery(
                "select first_name, last_name from actor")) {
                while (rs.next())
                    out.println(rs.getString(1) 
                        + " " + rs.getString(2));
            }
        }
    }
}

Observe cómo estamos usando la API JDBC directamente, sin conectarnos a ninguna base de datos.

Tenga en cuenta que trabajo para el proveedor de jOOQ, por lo que esta respuesta está sesgada.

Tenga cuidado, en algún momento, está implementando una base de datos completa

Lo anterior funciona para casos simples. Pero tenga en cuenta que, eventualmente, implementará una base de datos completa. Usted quiere:

  1. Verifique la sintaxis SQL.

Bien, burlándose de la base de datos como se muestra arriba, puede "verificar" la sintaxis, porque cada sintaxis que no haya previsto en la versión exacta que se enumera arriba será rechazada por cualquier enfoque burlón.

Puede implementar un analizador que analice SQL ( o, nuevamente, usar jOOQ ) y luego transformar la declaración SQL en algo que pueda reconocer más fácilmente y producir un resultado. Pero en última instancia, esto solo significa implementar una base de datos completa.

  1. Más importante aún, verifique que los datos estén seleccionados / actualizados / insertados correctamente, de acuerdo con una situación dada.

Esto dificulta aún más las cosas. Si ejecuta una inserción y luego actualiza, el resultado es obviamente diferente de actualizar primero, luego insertar, ya que la actualización puede o no afectar la fila insertada.

¿Cómo se asegura de que esto suceda cuando se "burla" de una base de datos? Necesita una máquina de estado que recuerde el estado de cada tabla "simulada". En otras palabras, implementará una base de datos.

Burlarse solo te llevará hasta aquí

Como mencionó Piotrek , burlarse solo te llevará hasta aquí. Es útil en casos simples cuando necesita interceptar solo unas pocas consultas muy conocidas. Es imposible, si quiere simular la base de datos de un sistema completo. En ese caso, use una base de datos real, idealmente el mismo producto que está usando en producción.

Lukas Eder
fuente
1

Creo que mi marco Acolyte se puede usar para dicha maqueta de base de datos: https://github.com/cchantep/acolyte .

Permite ejecutar Java existente (para pruebas) con conexiones que usted maneja para consultar / actualizar: devolver los conjuntos de resultados apropiados, actualizar el recuento o advertir según los casos de ejecución.

cchantep
fuente
0

Bueno, para empezar, ¿está utilizando alguna capa ORM para el acceso a la base de datos?
Si no es así: entonces lo que está pensando no sería de utilidad. ¿De qué sirve probar cuando no está seguro de que el SQL que está disparando funcionará con su base de datos en producción, ya que en los casos de prueba está usando otra cosa?
En caso afirmativo: entonces puede ver varias opciones señaladas.

Khangharoth
fuente