X
BLOG

Burlarse en Java: cómo automatizar una prueba unitaria de Java, incluidas las burlas y las afirmaciones

Burlarse en Java: cómo automatizar una prueba unitaria de Java, incluidas las burlas y las afirmaciones Tiempo de leer: 6 minutos
¿Qué es una burla en Java? Puede generar automáticamente una prueba unitaria con un solo clic de botón, incluidas todas las burlas y validaciones.

Las buenas pruebas unitarias son una excelente manera de asegurarse de que su código funcione hoy y continúe funcionando en el futuro. Un conjunto completo de pruebas, con una buena cobertura basada en código y comportamiento, puede ahorrarle a una organización mucho tiempo y dolores de cabeza. Y, sin embargo, no es raro ver proyectos en los que no se escriben suficientes pruebas. De hecho, algunos desarrolladores incluso han argumentado completamente en contra de su uso.

¿Qué hace una buena prueba unitaria?

Hay muchas razones por las que los desarrolladores no escriben suficientes pruebas unitarias. Una de las principales razones es la cantidad de tiempo que tardan en construir y mantener, especialmente en proyectos grandes y complejos. En proyectos complejos, a menudo una prueba unitaria necesita instanciar y configurar muchos objetos. Esto lleva mucho tiempo configurarlo y puede hacer que la prueba sea tan compleja (o más compleja) que el código que está probando.

Veamos un ejemplo en Java:

público LoanResponse requestLoan (LoanRequest solicitud de préstamo, LoanStrategy parte.)
{     LoanResponse response = new LoanResponse();     response.setApproved(true);     if (loanRequest.getDownPayment().compareTo(loanRequest.getAvailableFunds()) > 0) {        response.setApproved(false);        response.setMessage("error.insufficient.funds.for.down.payment");        return response;     }     if (strategy.getQualifier(loanRequest) < strategy.getThreshold(adminManager)) {         response.setApproved(false);         response.setMessage(getErrorMessage());     }     return response; }

Aquí tenemos un método que procesa un Solicitud de préstamo, generando un LoanResponse. Nota la LoanStrategy argumento, que se utiliza para procesar el Solicitud de préstamo. El objeto de estrategia puede ser complejo: puede acceder a una base de datos, un sistema externo o lanzar un Excepción en tiempo de ejecución. Para escribir una prueba para pedir prestamo(), Necesito preocuparme por el tipo de LoanStrategy Estoy probando y probablemente necesite probar mi método con una variedad de LoanStrategy implementaciones y Solicitud de préstamo configuraciones.

Una prueba unitaria parapedir prestamo()puede verse así:

@Test public void testRequestLoan() throws Throwable {    // Set up objects    DownPaymentLoanProcessor processor = new DownPaymentLoanProcessor();    LoanRequest loanRequest = LoanRequestFactory.create(1000, 100, 10000);    LoanStrategy strategy = new AvailableFundsLoanStrategy();    AdminManager adminManager = new AdminManagerImpl();    underTest.setAdminManager(adminManager);    Map<String, String> parameters = new HashMap<>();    parameters.put("loanProcessorThreshold", "20");    AdminDao adminDao = new InMemoryAdminDao(parameters);    adminManager.setAdminDao(adminDao);    // Call the method under test    LoanResponse response = processor.requestLoan(loanRequeststrategy);    // Assertions and other validations 

Como puede ver, hay una sección completa de mi prueba que solo crea objetos y configura parámetros. No era obvio mirar el pedir prestamo() método qué objetos y parámetros deben configurarse. Para crear este ejemplo, tuve que ejecutar la prueba, agregar alguna configuración, luego volver a ejecutar y repetir el proceso una y otra vez. Tuve que dedicar demasiado tiempo a averiguar cómo configurar el Gerente de administración y el LoanStrategy en lugar de centrarme en mi método y en lo que había que probar allí. Y todavía necesito ampliar mi prueba para cubrir más Solicitud de préstamo casos, más estrategias y más parámetros para AdminDao.

Además, al usar objetos reales para probar, mi prueba en realidad está validando más que solo el comportamiento de pedir prestamo() - Estoy dependiendo del comportamiento de AvailableFundsLoanStrategy, AdminManagerImply AdminDao para que se ejecute mi prueba. Efectivamente, también estoy probando esas clases. En algunos casos, esto es deseable, pero en otros casos no lo es. Además, si una de esas otras clases cambia, la prueba puede comenzar a fallar aunque el comportamiento de pedir prestamo() no cambió. Para esta prueba, preferimos aislar la clase bajo prueba de sus dependencias.

¿Qué es la burla en Java?

Una solución al problema de la complejidad es burlarse de esos objetos complejos. Para este ejemplo, comenzaré usando un simulacro para el LoanStrategy parámetro:

@Prueba

vacío público testRequestLoan () tiros Lanzable {
    // Configurar objetos 
    Procesador de Préstamo de Pago Inicial procesador = Un nuevo DownPaymentLoanProcessor (); Solicitud de préstamo solicitud de préstamo = LoanRequestFactory.create (1000, 100, 10000); LoanStrategy parte. = Mockito.mock (LoanStrategy.clase); Mockito. Cuando (parte..getQualifier (cualquier (LoanRequest.clase))). luegoReturn (20.0d); Mockito. Cuando (parte..getThreshold (cualquier (AdminManager.clase))). luegoReturn (20.0d);

    // Llamar al método bajo prueba
    LoanResponse respuesta = procesador.requestLoan (solicitud de préstamoparte.);

    // Afirmaciones y otras validaciones
}

Veamos lo que está sucediendo aquí. Creamos una instancia simulada de LoanStrategy usar Mockito.mock (). Ya que sabemos que getQualifier () y getThreshold () será llamado en la estrategia, definimos los valores de retorno para esas llamadas usando Mockito.Cuando (…) .entoncesReturn (). Para esta prueba, no nos importa lo que Solicitud de préstamo los valores de la instancia son, ni necesitamos un valor real Gerente de administración más porque Gerente de administración solo fue usado por el real LoanStrategy.

Además, dado que no estamos usando un LoanStrategy, no nos importa cuáles sean las implementaciones concretas de LoanStrategy podría hacer. No necesitamos configurar entornos de prueba, dependencias u objetos complejos. Estamos enfocados en probar pedir prestamo() - no LoanStrategy or Gerente de administración. El código de flujo del método bajo prueba es controlado directamente por el simulacro.

Esta prueba es mucho más fácil de escribir con Mockito de lo que hubiera sido si tuviera que crear un complejo LoanStrategy ejemplo. Pero todavía hay algunos desafíos:

  • Para aplicaciones complejas, las pruebas pueden requerir muchos simulacros
  • Si es nuevo en Mockito, necesita aprender su sintaxis y patrones
  • Es posible que no sepa qué métodos deben burlarse
  • Cuando la aplicación cambia, las pruebas (y simulaciones) también deben actualizarse

Resolver desafíos de simulación con un generador de pruebas unitarias de Java

Diseñamos Parasoft Jtest para ayudar a abordar los desafíos anteriores. El módulo de prueba unitaria Parasoft Jtest, una solución empresarial para pruebas de Java que ayuda a los desarrolladores a gestionar los riesgos del desarrollo de software Java.

En el lado de las pruebas unitarias, Parasoft Jtest lo ayuda a automatizar algunas de las partes más difíciles de crear y mantener pruebas unitarias con simulacros. Para el ejemplo anterior, puede generar automáticamente una prueba para pedir prestamo() con un solo clic de botón, incluidas todas las burlas y validaciones que ve en la prueba de ejemplo.


Aquí, utilicé la acción "Regular" en Parasoft Jtest Asistente de prueba unitaria Barra de herramientas para generar la siguiente prueba:

@Test public void testRequestLoan() throws Throwable {     // Given     DownPaymentLoanProcessor underTest = new DownPaymentLoanProcessor();     // When doble fondos disponibles = 0.0d// UTA: valor predeterminado doble depósito = 0.0d// UTA: valor predeterminado doble monto del préstamo = 0.0d// UTA: valor predeterminado     LoanRequest loanRequest = LoanRequestFactory.create(availableFundsdownPaymentloanAmount);     LoanStrategy strategy = mockLoanStrategy();     LoanResponse result = underTest.requestLoan(loanRequeststrategy);    // Then    // assertNotNull(result); }

Todas las burlas de esta prueba ocurren en un método auxiliar:

estática privada LoanStrategy mockLoanStrategy () tiros {LoanStrategy desechable parte. = simulacro (LoanStrategy.clase);
    doble getQualifierResult = 0.0d; // UTA: valor predeterminado
    cuando(parte..getQualifier (cualquier (LoanRequest.clase))). luegoReturn (getQualifierResult);

    doble getThresholdResult = 0.0d; // UTA: valor predeterminado
    cuando(parte..getThreshold (cualquier (AdminManager.clase))). luegoReturn (getThresholdResult);

    retorno parte.; }

Todas las burlas necesarias están configuradas para mí: Parasoft Jtest detectó las llamadas al método a getQualifier () y getThreshold () y se burló de los métodos. Una vez que configuro los valores en mi prueba unitaria para fondos disponibles, depósito, etc., la prueba está lista para ejecutarse (¡también podría generar una prueba parametrizada para una mejor cobertura!). Tenga en cuenta también que el asistente proporciona una guía sobre qué valores cambiar mediante sus comentarios, "UTA: valor predeterminado", lo que facilita las pruebas.

Esto ahorra mucho tiempo en la generación de pruebas, especialmente si no sé qué necesita ser burlado o cómo usar la API de Mockito.

Manejo de cambios de código

Cuando la lógica de la aplicación cambia, las pruebas a menudo también deben cambiar. Si la prueba está bien escrita, debería fallar si actualiza el código sin actualizar la prueba. A menudo, el mayor desafío al actualizar la prueba es comprender qué se debe actualizar y cómo realizar exactamente esa actualización. Si hay muchas simulaciones y valores, puede ser difícil rastrear cuáles son los cambios necesarios.

Para ilustrar esto, hagamos algunos cambios en el código bajo prueba:

público LoanResponse requestLoan (LoanRequest solicitud de préstamo, LoanStrategy parte.) { ... Cuerda resultado = parte..validar(solicitud de préstamo);
    if (resultado != nulo &&!resultado.esta vacio()) {
        respuesta.setApproved (false);
        respuesta.setMessage (resultado);
        retorno respuesta; } ...
    retorno respuesta; }

Hemos agregado un nuevo método a LoanStrategy - validar (), y ahora lo están llamando desde pedir prestamo(). Es posible que sea necesario actualizar la prueba para especificar qué validar() debería volver.

Sin cambiar la prueba generada, ejecutémosla dentro del Asistente de prueba unitaria de Parasoft Jtest:

Parasoft Jtest detectó que validar() fue llamado a los burlados LoanStrategy argumento durante mi ejecución de prueba. Dado que el método no ha sido configurado para el simulacro, el asistente recomienda que yo simule el validar() método. La acción de solución rápida "Mock it" actualiza la prueba automáticamente. Este es un ejemplo simple, pero para el código complejo en el que no es fácil encontrar el simulacro que falta, la recomendación y la solución rápida pueden ahorrarnos mucho tiempo de depuración.

Después de actualizar la prueba usando la solución rápida, puedo ver el nuevo simulacro y establecer el valor deseado para validateResult:

estática privada LoanStrategy mockLoanStrategy () tiros {LoanStrategy desechable parte. = simulacro (LoanStrategy.clase); Cuerda validateResult = ""// UTA: valor predeterminado
    cuando(parte..validate (cualquier (LoanRequest.clase))). luegoReturn (validateResult);
    doble getQualifierResult = 20.0 d; cuando(parte..getQualifier (cualquier (LoanRequest.clase))). luegoReturn (getQualifierResult);

    doble getThresholdResult = 20.0 d; cuando(parte..getThreshold (cualquier (AdminManager.clase))). luegoReturn (getThresholdResult);
    retorno parte.; }

Puedo configurar validateResult con un valor no vacío para probar el caso de uso donde el método ingresa al nuevo bloque de código, o puedo usar un valor vacío (o nulo) para validar el comportamiento cuando no se ingresa el nuevo bloque.

Analizar el flujo de prueba

El asistente también proporciona algunas herramientas útiles para analizar el flujo de prueba. Por ejemplo, aquí está el árbol de flujo para nuestra ejecución de prueba:

El Parasoft Jtest Asistente de prueba unitariaÁrbol de flujo, que muestra las llamadas realizadas durante la ejecución de la prueba

Cuando se ejecutó la prueba, puedo ver que la prueba creó un nuevo simulacro para LoanStrategy, y se burló del validar(), getQualifier ()y getThreshold () métodos. Puedo seleccionar llamadas a métodos y ver (en la vista Variables) qué argumentos se enviaron a esa llamada y qué valor se devolvió (o excepciones arrojadas). Al depurar pruebas, esto puede ser mucho más fácil de usar y comprender que buscar en archivos de registro.

Resumen

De modo que puede automatizar muchos aspectos de las pruebas unitarias. Parasoft Jtest lo ayuda a generar pruebas unitarias con menos tiempo y esfuerzo, lo que lo ayuda a reducir la complejidad asociada con las burlas. También hace muchos otros tipos de recomendaciones para mejorar las pruebas existentes basadas en datos de tiempo de ejecución, y tiene soporte para pruebas parametrizadas, pruebas de aplicación Spring y PowerMock (para simular métodos y constructores estáticos). Puedes conseguir un Prueba de 7 días gratis para comprobarlo en su propio entorno si hace clic a continuación:

Escrito por

Brian McGlauflin

Brian McGlauflin es un ingeniero de software en Parasoft con experiencia en el desarrollo de pila completa utilizando Spring y Android, pruebas de API y virtualización de servicios. Actualmente se centra en las pruebas de software automatizadas para aplicaciones Java con Parasoft Jtest.

Reciba las últimas noticias y recursos sobre pruebas de software en su bandeja de entrada.

Prueba Parasoft