Accediendo a BBDD con SoapUI no PRO

Durante la ejecución de nuestras pruebas nos encontramos en ocasiones con la necesidad de acceder a la BBDD para consultar, cambiar o crear algún dato que nos permita completar nuestro test.

La gratuita de SoapUI (al menos la 4.0.1 que estoy usando) no tiene habilitada las opciones de DataSource TestStep, DataSourceLoop TestStep ni DataSink TestStep, que me imagino que permitirán actuar sobre la BBDD (corregidme si me equivoco). No obstante, aun tenemos mecanismos que nos permiten acceder a la BBDD. Para ello acude en nuestra ayuda los scripts de Groovy.

Antes de nada debemos colocar nuestra versión java del driver jdbc de la BBDD a la que queremos acceder, en un lugar conocido. Yo sugiero colocarlo en la carpeta “ext” del SoapUI. Esta carpeta es para colocar nuestras librerías y en teoría para que estuvieran disponibles para su uso, pero por lo que he podido comprobar no están disponibles desde los scripts groovy, aun así la colocaré ahí para nuestro ejemplo.

Para ejecutar el script ya sabéis, añadid un Test Step del tipo Groovy Script en nuestro test suite.

Y aquí os pongo un ejemplo para el acceso a postgres:

//Declaramos el driver
def jarFile = "postgresql-9.1-901.jdbc4.jar"
//Cargamos el driver que habiamos colocado en la carpeta ext
com.eviware.soapui.support.ClasspathHacker.addURL( new URL("file:./ext/$jarFile") )

import groovy.sql.Sql
def urlConexion = "tu url de conexion"
def user = "usuario1"
def password = "12345"

def sql = Sql.newInstance(urlConexion, user, password, 'org.postgresql.Driver')
def id_selected = 1
//Fijaos que usamos las $ para referirnos a las variables que tenemos definidas.
def query = "Select * from tablename where id = $id_selected "
def rows = sql.rows(query)

//Comprobación de que ha devuelto algún valor
assert rows.size() == 1

//Cerramos la conexión
sql.close();

Mas información sobre los comandos sql de Groovy: Groovy Sql

Anuncios

SoapUI Generación de propiedades

Hay varias formas de generar datos dinámicos en SoapUI que posteriormente podremos usar en nuestras peticiones WS.

Propiedades auto-generadas.

SoapUI permite declarar propiedades cuyo valor es interpretado, generando un valor cada vez que se consulta.

En la imagen se genera un bloque TestSuite, pero también se puede generar en un bloque TestCase.

Propiedad del test-suite autogenerada

Propiedad auto-generada

Como ya supondréis la cadena ${=Math.random()*100} generará un valor numérico aleatorio con una parte entera <= 100.

Para acceder a este valor usaremos la cadena ${#TestSuite#autogenerado} en el nodo donde queremos que se inserte.

<cur:amount>${#TestSuite#autogenerado}</cur:amount>

Si hemos generado el valor en un bloque TestCase la cadena será similar ${#TestCase#autogenerado}

Propiedades auto-generadas 2

Existe otra forma de generar las propiedades, que los hace una vez en cada ejecución. Esta forma de generación es mas útil si pretendemos usar estos valores de forma repetida a lo largo del test. Esta generación la realizaremos con ayuda de un script. En este ejemplo usaremos la pestaña “SetupScript” del TestCase que nos permite introducir este script.

SoapUI TestCase SetupScript

SoapUI TestCase SetupScript

Las sentencias de Setup Script se ejecutan antes de iniciar el test, por lo que tendremos al inicio de cada ejecución un nuevo valor en la propiedad autogenerado2.

Si usamos el botón play (en la imagen el botón verde al lado de Edit ) se ejecutará el script y podremos ver la propiedad generada en la pestaña Properties que ya hemos visto en la imagen anterior.

Si queremos tener mas visible en qué lugares se generan valores podemos añadir un paso del tipo Groovy Script y añadir ahí el mismo script que acabos de usar.

Groovy Script

Groovy Script

 

Eclipse + JUnit + java.lang.NoSuchFieldError: NULL

Estos días me he vuelto a tropezar con un error que me costó bastante tiempo resolver, no por la complejidad, sino por encontrar el motivo:

Antecedentes:

Tengo un proyecto A en eclipse que depende de otro B. El proyecto A tiene dependencias con JUnit 4.10 y la ejecución con “mvn test” funciona correctamente, aun así, Eclipse me devuelve este elocuente error:

java.lang.NoSuchFieldError: NULL
at org.junit.runners.ParentRunner.(ParentRunner.java:54)
at org.junit.runners.BlockJUnit4ClassRunner.(BlockJUnit4ClassRunner.java:55)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.(SpringJUnit4ClassRunner.java:104)

Reviso las dependencias con “mvn dependency:tree” para ver las librerías que se están incluyendo y efectivamente está presente la librería  junit-4.10.jar

¿Cómo es posible que mvn funcione y Eclipse no?  Pues algo extraño tiene que pasar con Eclipse. Reviso las dependencias que importa el proyecto, bloque Referenced Library usando la vista Package Explorer y la entrada sobre JUnit que tengo es junit-4.10.jar ¡Todo correcto! Entonces… ¿Qué está pasando?

Reviso el lanzamiento del Junit: Run As -> Run Preferences y compruebo que el Test Runner es JUnit4 ¿Entones? Entonces es cuando me doy cuenta que en el Classpath se está importando librerías del proyecto B, entre ellas la junit-3.8.jar

¿Por qué no prevalece la librería importada por el proyecto principal A? No lo sé.

Para solucionar el error anterior hay que eliminar esta incoherencia de librerías. Esto ya depende de como estén montado el proyecto A y el B. En mi caso, para solucionarlo e eliminado la referencia directa al proyecto B y hago la referencia al B.jar que se genera a partir de él y que no contiene la librería problemática.

Si tienes el proyecto B referenciado desde el pom como un jar, en vez de hacer “mvn eclipse:eclipse”  puedes hacer

mvn eclipse:clean eclipse:eclipse -Declipse.useProjectReferences=false

para que no se haga referencia a los proyectos del classpath.

Espero que esto os sirva de ayuda.

Test de Integración vs Test Unitarios

Introducción

Desde hace cierto tiempo la comunidad de desarrolladores y empresas del sector se están concienciando de la importancia de tener definido e implementado un sistema de pruebas sobre el software desarrollado. Para dar soporte a estas pruebas nacieron una serie de frameworks y herramientas como la familia xUnit y entornos de integración continua como Hudson.
¿Cómo se implementan estas pruebas? Básicamente podemos definir dos tipos, Pruebas de Integración y Pruebas Unitarias, cada una de las cuales tiene sus particularidades, comencemos por su definición.

Definición

Tomando las definiciones recopiladas en el blog de  PSDM Consulting podrían definirse como:

  • Prueba unitaria: prueba un único método de una clase. El alcance es muy reducido y está perfectamente acotado. Cualquier dependencia del módulo bajo prueba debe ser sustituida por un mock, o un stub.
  • Prueba de integración: prueba la interacción entre dos o más elementos, que pueden ser clases, módulos, paquetes, subsistemas, etc… incluso la interacción del sistema con el entorno de producción.

Ventajas y desventajas

Conociendo las ventajas y desventajas quizás podamos tener los datos suficientes para tomar la decisión de cuándo usarlas ya que hay distintas posiciones frente al tipo de test a utilizar.

Velocidad de ejecución

Definiendo velocidad por el tiempo que se tarda en ejecutar una prueba de este tipo, podemos afirmar sin realizar ninguna prueba empírica que lo Test de Integración deben ser más lentos de ejecutar que los Test Unitarios, ya que los Test de Integración ejecutan más líneas de código que un Test Unitario que se dedica solo a un método. ¿Cuál es la diferencia de tiempos? Esto depende tanto del código como del framework utilizado para las pruebas y mocks ya que un framework con objetos mocks muy lentos podrían penalizar los tiempos de ejecución de los Test Unitarios e igualar estos tiempos.

Velocidad de implementación

Los Test Unitarios consisten en las pruebas de métodos específicos y atañe sólo al código encerrado dentro de dicho método. Cada acceso al “exterior” es simulado mediante un mock.
Resumiendo mucho tendríamos que implementar un test por cada conjunto de valores de entrada a probar, así como crear los mocks necesarios para cada test.
En los Test de Integración evitamos, en la mayoría de las ocasiones, tener que crear mocks. La complejidad radica en crear las condiciones iniciales (datos iniciales en la BBDD u otros sistemas)  para ejecutar el test, que como apunta Adam Kolawa son fundamentales para que los Test de Integración sean realmente válidos.
Suponiendo que solo tenemos una interfaz para acceder a la aplicación, se debería implementar un conjunto de Test de Integración (en función de los valores de entrada y condiciones iniciales) por cada método esta interfaz, mientras que para los Test Unitarios, tendríamos que implementar un conjunto de test por cada método de cada clase que compone el software que estamos desarrollando.
Imaginemos que implementamos un solo test por método y que nuestra arquitectura corresponde al siguiente esquema, teniendo cada nivel una sola clase.
Service -> Manager -> Dao

Suponiendo que cada clase tiene 5 métodos necesitaríamos: 5 Test de Integración, frente a 15 Test Unitarios. En un caso real, tendríamos variasias clases por nivel sin contar con las clases de utilidades, esta diferencia se hace notoriamente mayor.

Cobertura de código

Si definimos cobertura como las líneas de código recorridas durante la ejecución de un Test, podemos decir que los Tests de Integración recorren mucho más código que los Unitario ya que pasan por más regiones de código. Sin embargo y dependiendo del diseño del código, habrá regiones de código que requieran de complejas condiciones iniciales para que el código sea alcanzado por un Test de Integración lo cual es un punto “en contra” de los Test de Integración, y lo pongo entre comillas por una razón que veremos más adelante.

Mantenibilidad de los Tests

Este es un punto muy controvertido. Mientras que algunos mantienen que los test unitarios son más fáciles de mantener otros, que los test de integración son más fáciles de mantener pero la verdad que siempre dependerá de los tipos de cambios a los que se vean sometidos el código.
Veamos varios escenarios y cómo afectan a los Tests.
– Cambios en clases intermedias que no afectan a los resultados, por ejemplo una refactorización que no afecta a los interfaces: En este caso los test de integración no tienen por qué verse afectados ya que tratan al código como casi una caja negra, sin embargo todas las clases y métodos modificados requerirán de modificación de test unitarios y mocks usados.
– Cambios en el comportamiento general de la aplicación: En este caso tanto los test de integración que esperaban cierto resultado se verán afectados como  los test unitarios que probaban las clases y métodos modificados.

Utilidad para la Mantenibilidad del código

Durante el desarrollo  del software los Test Unitarios nos dan información de qué método es el que está fallando o no da el resultado esperado por el Test. Esta cualidad es muy ventajosa para el desarrollador ya que el Test lo conduce directamente hacia el método a corregir.
Los Test de Integración dan una visión más global convirtiéndose en una poderosa herramienta para realizar las pruebas de regresión. Son capaces de detectar errores producidos durante el tratamiento de la información que no se producen de forma local a un método. Como desventaja , el Test de Integración no conduce al desarrollador al método que produce el error y requiere de depuración por parte de este, sin embargo detectar el error que un Test Unitario no podría es un a ventaja a tener en cuenta.

Proximidad a las pruebas de aceptación

Una de las ventajas que ofrecen los test de integración que no pueden aportar los test unitarios es la implementación de las pruebas de aceptación sobre el software.

Cobertura falseada por los test unitarios

Como comentaba en el apartado de cobertura, se podrían dar regiones de código que pueden ser difícilmente alcanzable por las pruebas de integración. Si estas regiones estuvieran recorridas mediante pruebas unitarias se estaría ocultando este dato, es decir, no conoceríamos regiones de código que nunca se recorren y que pueden ser reimplementadas para que sea un código realmente útil.

Veracidad de las pruebas

De todos es sabido que imposible implementar, en un tiempo razonable, un conjunto de pruebas que garanticen que un software de cierto tamaño no falle. Y esta imposibilidad aumenta con el tamaño del proyecto. ¿Qué pruebas pueden cubrir más errores? Para tratar de contestar a esta pregunta planteemos la siguiente situación:
Tenemos 5 clases que se llaman en cadena para realizar una operación determinada.
A->B->C->D->E
Si cada clase tiene una probabilidad de realizar su cometido con éxito de un 95%, tenemos que la probabilidad total, al ser llamadas encadenadas, es el producto de sus probabilidades, lo que da un resultado de: 0.95^5 = 0.77. Tenemos un 77% de probabilidad de éxito.
¿Qué significa esto? Podríamos decir, que los test de integración tienen más probabilidades de fallar, y por lo tanto de detectar bugs.
A esto habría que añadir que los test de integración prueban la conexión entre métodos, validando que las clases se invoquen de forma correcta entre ellas.

Conclusiones

Los Test de Integración dan una información global que no puede ser ignorada:
– Pruebas de aceptación.
– Pruebas de regresión.
– Pruebas funcionales.
– Menor número de test para probar la aplicación.
– Mantenibilidad más sencilla.
Los Test Unitarios proporcionan ventajas que tampoco pueden ser ignoradas:
– Es una forma sencilla de probar el código de forma exhaustiva e independiente de otros artefactos.
– Dirigen al desarrollador a la porción de código que falla de forma directa.

En conclusión y en mi opinión los Test de Integración deberían estar siempre en todos los desarrollos mientras que los Test Unitarios deberían ser usados para probar métodos complejos o críticos.

Referencias:

[1]http://psdm.co/2012/01/diferencias-entre-pruebas-unitarias-y-de-integracion/
[2]http://www.codeproject.com/Articles/235724/Integration-vs-unit-tests
[3]http://ix.cs.uoregon.edu/~michal/book/index.html
[4]http://geosoft.no/development/unittesting.html
[5]http://www.parasoft.com/jsp/products/article.jsp;jsessionid=baa4J9YnmGUi0g?articleId=2869&redname=unit-testing-best-practices&referred=unit-testing-best-practices

Usando MockCursor de Android

Estos dias me he decidido a implementar algunos tests en Android y una de las implementaciones que creo que ha merecido un post ha sido la implementación de un test que ha requerido la personalización de MockCursor, así que aquí está:

Contextualizo un poco:  La idea del test es probar un método que hace uso de un cursor el cual recibe por parámetro.  El método a probar utiliza este cursor para recorrer cada fila resultado y acceder a sus columnas.

El método a probar sería algo como el siguiente (prestad solo atención al uso del cursor)

private BigDecimal calcularTotal(Cursor cursor){
    BigDecimal total = new BigDecimal(0,MathContext.DECIMAL32);
    int unidades = 0;
    float precio = 0;
    if(cursor.moveToFirst()){
        if(DatabaseHelper.TRUE == cursor.getInt(cursor.getColumnIndex(ProductoDbAdapter.KEY_COMPRADO))){
            unidades = cursor.getInt(cursor.getColumnIndex(ProductoDbAdapter.KEY_UNIDADES));
            unidades = unidades <=1? 1:unidades;
            precio = cursor.getFloat(cursor.getColumnIndex(ProductoDbAdapter.KEY_PRECIO));
            if(precio > 0){
                total = total.add(new BigDecimal(unidades*precio));
            }
        }
        while(cursor.moveToNext()){
            if(DatabaseHelper.TRUE == cursor.getInt(cursor.getColumnIndex(ProductoDbAdapter.KEY_COMPRADO))){
                unidades = cursor.getInt(cursor.getColumnIndex(ProductoDbAdapter.KEY_UNIDADES));
                unidades = unidades <=1? 1:unidades;
                precio = cursor.getFloat(cursor.getColumnIndex(ProductoDbAdapter.KEY_PRECIO));
                if(precio > 0){
                    total = total.add(new BigDecimal(unidades*precio));
                }
            }
        }
    }
    return total;
}//Fin calcularTotal

Para testear este método de forma unitaria, es decir, aislada del resto, necesitamos que sea independiente del Cursor, y ahí es donde entra MockCursor para echarnos un cable. He de decir que MockCursor no es un JMock,  solo es una clase que implementa la interfaz  Cursor devolviendo UnsupportedOperationException para cada llamada de sus métodos., por lo tanto para usarlo hay que sobreescribir los métodos que vamos a usar.

Otro punto a destacar es que el método a probar es privado y no podremos probarlo tal cual. Mas adelante veremos como.

Bien, para probar nuestro método debemos personalizar el MockCursor, es decir, extenderlo y personalizar los métodos que queremos usar. Hay varias formas de hacerlo, una clase anónima, una clase anidada, etc. El lector que decida que solución es la que mas le convence.  Yo usaré una clase anidada al test. La idea de la implementación es simular el recorrido por los resultados obtenidos por una supuesta consulta y devolver los valores cuando se acceda a las columnas de cada resultado. El método a probar solo accede a tres columnas, así que no es demasiado complicado.

public class MockCursorAdapted extends MockCursor{
        int actualIndex = 0; //Indice para el recorrido por los resultados
        Map<Integer,String> entry;
        List<Map<Integer,String>> entryList;
        Map <String,Integer> columnIndexes;
        //Value initialization
        {
            //Column indexes: Se configuran las posiciones de las columnas en función de sus nombres.
            columnIndexes = new HashMap<String,Integer>();
            columnIndexes.put(ProductoDbAdapter.KEY_UNIDADES, 0);
            columnIndexes.put(ProductoDbAdapter.KEY_PRECIO, 1);
            columnIndexes.put(ProductoDbAdapter.KEY_COMPRADO, 2);
            //Creación de los resultados. Se crea una lista de mapas. Cada entrada en la lista es
            //un resultado. Y cada resultado, es un mapa con los valores de cada columna.  
            entryList = new ArrayList<Map<Integer,String>>();

            Map<Integer,String> m = new HashMap<Integer,String>();
            m.put(getColumnIndex(ProductoDbAdapter.KEY_UNIDADES),"1");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_PRECIO),"22.40");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_COMPRADO),"1");
            entryList.add(m);

            m = new HashMap<Integer,String>();
            m.put(getColumnIndex(ProductoDbAdapter.KEY_UNIDADES),"2");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_PRECIO),"5.50");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_COMPRADO),"1");
            entryList.add(m);

            m = new HashMap<Integer,String>();
            m.put(getColumnIndex(ProductoDbAdapter.KEY_UNIDADES),"1");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_PRECIO),"100.0");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_COMPRADO),"1");
            entryList.add(m);
        }

        @Override
        public String getString(int columnIndex) {
            return getValue(columnIndex);
        }

        @Override
        public float getFloat(int columnIndex) {
            return Float.parseFloat(getValue(columnIndex));
        }

        @Override
        public int getInt(int columnIndex) {
            return Integer.parseInt(getValue(columnIndex));
        }

        /**
         * Obtención del valor de la columna indicada para la fila actual
         * @param columnIndex
         * @return
         */
        private String getValue(int columnIndex){
            entry = entryList.get(actualIndex);
            String value = entry.get(columnIndex);
            if(value == null){
                Log.e(TAG, "Sin valor para columna "+columnIndex+" y actualIndex "+actualIndex+" entrada "+entry.toString());
            }
            return value;
        }

        @Override
        public int getColumnIndex(String columnName) {
            return columnIndexes.get(columnName);
        }

        @Override
        public boolean moveToFirst() {
            return true;
        }

        @Override
        public boolean moveToNext() {
            // TODO Auto-generated method stub
            return ++actualIndex < entryList.size();
        }
    }//FIN MockCursorAdapted

Bien, ya tenemos el Mock del cursor personalizado. Creando la clase que ejecutará el test ya lo tendriamos resuelto… bueno… falta el acceso al método privado…

Bien, la forma de ejecutar el método privado es mediante reflexión, digamos que le pedimos a la clase que nos de el método que tiene dicho nombre. De forma muy resumida y sin controlar excepciones:

 Cursor cursorAdapted = new MockCursorAdapted();
 ActividadConMetodoCalcularTotal mActivity = new ActividadConMetodoCalcularTotal();
 Method m = this.mActivity.getClass().getDeclaredMethod("calcularTotal", new Class[]{Cursor.class});
 m.setAccessible(true); //Le decimos que nos deje ejecutarlo desde fuera 😛
 BigDecimal resultado = (BigDecimal) m.invoke(mActivity , new Object[]{cursorAdapted});

Listo!

Un apunte mas:

OJO! MockCursor está disponible a partir de la API 8, por lo que debeis ejecutar los test en un emulador con la versión correspondiente (creo que a partir de la 2.3.3). Si no lo haceis, dará un error en ejecución de NoClassDefFoundError sobre la clase personalizada (en este caso $MockCursorAdapted). Mirando mas detenidamente las trazas se puede ver al principio un warning “unable to resolve superclass” refiriendose a MockCursor… este error me ha tenido ocupado un buen rato.

Espero que os haya gustado la aportación, suerte con los test!