Skip to main content

Cómo escribir pruebas unitarias en Java

Escrito por

Lewis Gavin

feature java dto

18 de noviembre de 2022

0 minutos de lectura

Las pruebas son una práctica recomendada fundamental al desarrollar software. Las pruebas unitarias son una de las muchas estrategias que podemos usar para asegurarnos de que nuestro código funcione correctamente y tenga un rendimiento óptimo. Como desarrolladores, podemos escribir pruebas unitarias para comprobar componentes individuales (unidades) del código de una aplicación, como un método específico. La idea es escribir una o más pruebas unitarias para cada sección del código y ejecutarlas cada vez que se realice un cambio, para detectar defectos en cuanto se introduzcan en la base de código. Para el código nuevo, las pruebas deben agregarse al mismo tiempo que se escribe el código (o incluso antes). Para el código existente, si aún no hay pruebas, deben agregarse para cubrir gran parte —si no todas— las rutas de ejecución de la lógica de la aplicación.

Las pruebas unitarias buscan eliminar todas las dependencias externas y confirmar que una parte específica del código se comporte como se espera en distintas situaciones. Esto podría implicar llamar a un método varias veces, cada una con parámetros nuevos, y comprobar que devuelva el valor correcto, genere una excepción o realice una tarea específica.

Entre los frameworks de pruebas unitarias disponibles, muchos desarrolladores de Java usan JUnit. En esta publicación, aprenderemos a instalar y usar JUnit 5 para escribir pruebas unitarias para código Java. Usaremos el entorno de desarrollo integrado (IDE) VSCode para escribir las pruebas y Java 11 con Maven para ejecutarlas. Sin embargo, herramientas como Eclipse e IntelliJ funcionan de manera similar y te permitirán seguir los pasos.

Para conocer más herramientas con las que revisar tu código Java, consulta nuestras herramientas de revisión de código Java o nuestro artículo sobre seguridad en Java para obtener más conocimientos básicos.

Cómo escribir tu primera prueba unitaria en Java

En esta sección, instalaremos el framework JUnit y escribiremos algunas pruebas unitarias. Más adelante, ejecutaremos las pruebas en este proyecto de código de ejemplo. El proyecto ya está configurado con el código de la aplicación y una plantilla para una clase de prueba que contiene todo lo necesario para empezar a agregar pruebas.

Primero, antes de empezar a escribir las pruebas, revisemos el código de ejemplo.

package com.snyk.unittestdemo;

public class App 
{
    public static void main( String[] args )
    {
        try {
            System.out.println(generateUpdateUserFirstNameStatement("John", "abcdef1234567"));
        }
        catch (Error e){
            e.printStackTrace();
        }
    }

    public static String generateUpdateUserFirstNameStatement(String firstName, String userId) throws Error{
        if(userId.contains(";") || firstName.contains(";")){
            throw new Error("parameters may contain SQL injection");
        }
        return "UPDATE TABLE user SET first_name = '" + firstName + "' WHERE user_id = '" + userId +"';" ;
    }

}

Nuestra aplicación tiene un método main que llama al método generateUpdateUserFirstNameStatement e imprime el resultado. Si una llamada al método generateUpdateUserFirstNameStatement genera un error, la aplicación captura la excepción e imprime el seguimiento de la pila.

El método generateUpdateUserFirstNameStatement es sencillo: recibe dos parámetros de tipo cadena y los usa para generar una instrucción SQL UPDATE. El código genera un error si alguno de los parámetros contiene un punto y coma, ya que probablemente la entrada contiene una inyección SQL.

Este método es un caso de uso ideal para las pruebas unitarias, ya que queremos confirmar que funcione en dos escenarios. Primero, cuando pasamos dos cadenas estándar como parámetros, esperamos que el resultado sea una cadena que contenga la instrucción SQL. Luego, cuando proporcionamos como segundo parámetro una cadena que contiene una inyección SQL, esperamos que el método genere un error. Podemos probar ambos casos con una prueba unitaria.

Para empezar, debemos instalar algunos paquetes de Maven, incluido maven-surefire-plugin, que incluye la biblioteca JUnit. También debemos indicarle a Maven que nuestra aplicación depende de JUnit al ejecutar las pruebas.

Abramos el archivo pom.xml proporcionado y agreguemos el siguiente código de dependencia en la sección correspondiente, en la línea 21.

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>

Cuando termines, guarda este archivo.

Ahora podemos usar Maven para instalar los paquetes. Para hacerlo en VSCode, expande el menú Maven en la parte inferior izquierda, haz clic derecho en el proyecto y selecciona install. Si usas otro IDE, el proceso puede ser distinto.

Visual Studio Code muestra el archivo pom.xml de un proyecto Java con una dependencia de prueba de JUnit 4.11 y el menú de comandos de Maven abierto

Si no sabes cómo hacerlo en tu IDE, siempre puedes instalar las dependencias desde la línea de comandos. En una terminal, cambia al directorio base del proyecto y ejecuta el siguiente comando de Maven para instalar las dependencias.

Nota: Si no tienes Maven instalado en tu equipo, sigue las instrucciones de instalación antes de ejecutar este comando.

mvn install -f pom.xml

Al terminar, Maven habrá instalado los complementos especificados en nuestro archivo pom.xml file, incluida la biblioteca JUnit. Al ejecutar las pruebas, Maven sabe que debe usar JUnit, que definimos como dependencia de prueba con <scope>test</scope> en la configuración.

Salida de terminal que muestra pruebas unitarias de Java sin fallas y el resultado verde «BUILD SUCCESS» de Maven.

Como podemos ver en el resultado anterior, al final del proceso de instalación se muestra información relacionada con las pruebas. Sin embargo, todavía no tenemos ninguna prueba configurada, por lo que se ejecutaron 0 pruebas. Cambiemos esto y agreguemos dos pruebas unitarias a nuestra aplicación.

Para empezar, abre el archivo src/test/java/com/snyk/unittestdemo/AppTest.java, que Maven ejecutará para correr nuestras pruebas. Primero, debemos importar los paquetes de JUnit necesarios. Agrega las siguientes dos importaciones al principio del archivo:

import org.junit.Test;

import static org.junit.Assert.assertTrue;

La primera importación nos da acceso a la anotación de prueba de JUnit, para que podamos definir nuestros métodos como pruebas. La segunda importación corresponde al método assertTrue que proporciona JUnit. Usamos este método para comprobar si una condición se evalúa como verdadera. Si es así, la prueba se aprobará; de lo contrario, fallará.

Agreguemos nuestra primera prueba, que comprobará que el método generateUpdateUserFirstNameStatement devuelva el resultado correcto cuando recibe dos cadenas normales. Agrega el siguiente método dentro de la clase AppTest.

@Test
public void shouldGenerateUpdateStatement()
{
    String actual = App.generateUpdateUserFirstNameStatement("John", "abcdef1234567");
    String expected = "UPDATE TABLE user SET first_name = 'John' WHERE user_id = 'abcdef1234567';";
    assertTrue( actual.equals(expected) );
}

Definimos el método como una prueba mediante la anotación @Test. Luego, usamos el método importado assertTrue para comprobar el resultado de nuestro método generateUpdateUserFirstNameStatement. Proporcionamos dos cadenas —John y abcdef1234567— y queremos comprobar que la función devuelva la cadena esperada. Si nuestro método funciona correctamente, esta prueba debería aprobarse.

Guarda el archivo y ejecuta la prueba para comprobar que se apruebe. En VSCode, puedes hacer clic derecho en el proyecto de Maven y seleccionar test.

Visual Studio Code muestra una prueba AppTest de Java JUnit y la salida de la terminal de Maven, con una prueba aprobada y una compilación exitosa

También puedes ejecutar este comando desde la terminal. Para hacerlo, abre la terminal, cambia al directorio del proyecto y ejecuta el siguiente comando.

mvn test -f pom.xml

Este comando le indica a Maven que compile la aplicación y ejecute las pruebas. El resultado de las pruebas debería mostrar que se ejecutó una prueba y que no hubo fallas.

Salida de la terminal que muestra una ejecución de pruebas unitarias de Java sin fallas, errores ni pruebas omitidas, seguida del mensaje BUILD SUCCESS.

Ahora modifiquemos ligeramente el método generateUpdateUserFirstNameStatement, cambiando la instrucción UPDATE que devolverá la función. Vuelve a ejecutar las pruebas para ver cómo Maven muestra una falla.

Salida de terminal que muestra una prueba unitaria de Java fallida, con un error de aserción en AppTest.shouldGenerateUpdateStatement y una compilación de Maven fallida.

El resultado cambia y nos indica que una prueba falló. Debajo del encabezado Results:, puedes ver la cantidad y los tipos de fallas. En este caso, los resultados indican el método de prueba generateUpdateUserFirstNameStatement y el número de línea donde ocurrió la falla (línea 14). Recuerda deshacer los cambios que hicieron que la función dejara de funcionar.

También podemos comprobar que nuestra función genere un error como se espera, con un método un poco distinto de assertTrue. Para empezar, agrega el siguiente método de prueba a la clase de prueba.

@Test(expected = Error.class)
public void shouldThrowErrorForSQLInjection()
{
    App.generateUpdateUserFirstNameStatement("John", "abcdef1234567; DROP TABLE user;");
}

Aquí usamos la anotación @Test para indicar un valor esperado. Esperamos que nuestro método genere una instancia de la clase Error, ya que así lo programamos para que maneje la recepción de una inyección SQL en cualquiera de los parámetros. Ahora podemos ejecutar las pruebas y comprobar que se aprueben.

Salida de terminal que muestra una ejecución exitosa de pruebas unitarias de Java: dos pruebas, sin fallas ni errores y un mensaje de BUILD SUCCESS.

Implementamos correctamente nuestras primeras pruebas unitarias de Java con JUnit. De esta manera, podemos asegurarnos de que nuestro código funcione como se espera y produzca los resultados correctos. Si no es así, sabremos que la prueba fallará.

Es fundamental proteger nuestras aplicaciones contra vulnerabilidades, como las que nos exponen a inyecciones SQL maliciosas. Agregar una prueba como esta puede evitar que un desarrollador introduzca esta vulnerabilidad en el futuro. Las pruebas nos dan confianza en nuestro código y ofrecen una red de seguridad para quienes desarrollen después. Obtén más información sobre las prácticas recomendadas de seguridad en Java aquí.

Snyk Code es otra herramienta que puede detectar prácticas de seguridad deficientes que los desarrolladores quizá desconozcan y para las que no tengan pruebas escritas. Esto incluye detectar código vulnerable a inyecciones SQL e información confidencial codificada de forma fija, como secretos de API e información de identificación personal.

La prueba define un contrato sobre lo que debe hacer el método. Si un desarrollador quiere actualizar el método generateUpdateUserFirstNameStatement y lo modifica accidentalmente —como hicimos en nuestra segunda prueba—, la prueba unitaria fallará e informará sobre el problema. Esta práctica agrega una capa de protección para evitar que se interrumpan funciones esenciales y que se implementen cambios inestables o peligrosos en una aplicación de producción.

Conclusión

Como aprendimos en este tutorial, escribir pruebas unitarias es una manera sencilla de asegurarnos de que nuestro código se comporte como se espera en distintos escenarios. Además, las pruebas unitarias ofrecen una capa de resistencia a fallas al trabajar en proyectos colaborativos, ya que evitan que otros desarrolladores interrumpan nuestro código por accidente o introduzcan vulnerabilidades. Aunque no son ni mucho menos la única forma de probar aplicaciones, las pruebas unitarias deberían formar parte del conjunto de herramientas de todo desarrollador.

Consulta estos artículos relacionados para aprender más sobre aspectos técnicos de Java:

Comienza con Capture the Flag

Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.

Leer más

Blog

Los modelos de frontera encontraron las vulnerabilidades. Solo el atacante encontró las cadenas.

El análisis estático encontró las fallas, pero solo las pruebas de ataque en vivo demostraron cómo podían encadenarse para provocar brechas. Una comparación de Evo COS, Claude Security y Claude Code Security.

feature insights context
Blog

Los ataques autónomos ya están aquí. La defensa debe estar a su altura.

Los atacantes autónomos están reduciendo el tiempo disponible para defenderse. Descubre cómo el descubrimiento, la corrección, la validación y la prevención continuos pueden ayudar a los equipos de seguridad a seguirles el ritmo.

Blog

Por qué los agentes de programación con IA siguen generando fallas de control de acceso

Los agentes de programación con IA pueden generar lógica de autorización que compila y supera la revisión, pero expone los datos de un inquilino a otro. Descubre por qué es difícil detectar el control de acceso roto y cómo prevenirlo.