Seminario web destacado: MISRA C++ 2023: todo lo que necesita saber | Vea ahora

Cómo crear pruebas con parámetros de JUnit

Foto de cabeza de Brian McGlauflin,
19 de septiembre de 2018
7 min leer

Escribir código de prueba parametrizado puede suponer mucho trabajo porque cada prueba necesita una gran cantidad de código escrito correctamente. Afortunadamente, así es como puede usar Parasoft Jtest para generar automáticamente sus pruebas parametrizadas sin escribir códigos repetitivos.

Las pruebas parametrizadas son una buena forma de definir y ejecutar múltiples casos de prueba, donde la única diferencia entre ellos son los datos. Aquí, analizamos tres marcos diferentes que se usan comúnmente con las pruebas JUnit.

Al escribir pruebas unitarias, es común inicializar los parámetros de entrada del método y los resultados esperados en el propio método de prueba. En algunos casos, basta con utilizar un pequeño conjunto de entradas; sin embargo, hay casos en los que necesitamos usar un gran conjunto de valores para verificar toda la funcionalidad en nuestro código. Las pruebas parametrizadas son una buena manera de definir y ejecutar múltiples casos de prueba, donde la única diferencia entre ellos son los datos. Pueden validar el comportamiento del código para una variedad de valores, incluidos los casos de borde. La parametrización de las pruebas puede aumentar la cobertura del código y proporcionar la confianza de que el código está funcionando como se esperaba.

Hay una serie de buenos marcos de parametrización para Java. En este artículo, veremos tres marcos diferentes que se usan comúnmente con las pruebas JUnit, con una comparación entre ellos y ejemplos de cómo se estructuran las pruebas para cada uno. Finalmente, exploraremos cómo simplificar y acelerar la creación de pruebas parametrizadas.

Marcos de prueba parametrizados JUnit

Comparemos los 3 marcos más comunes: JUnit 4, JunitParams y JUnit 5. Cada marco de parametrización JUnit tiene sus propias fortalezas y debilidades.

Unidad 4

Pros:

  • Este es el marco de parametrización integrado en JUnit 4, por lo que no requiere dependencias externas adicionales.
  • Es compatible con versiones anteriores de Java (JDK 7 y anteriores).

Contras:

  • Las clases de prueba usan campos y constructores para definir parámetros, lo que hace que las pruebas sean más detalladas.
  • Requiere una clase de prueba separada para cada método que se prueba.

JunitParams

Pros:

  • Simplifica la sintaxis de los parámetros al permitir que los parámetros se pasen directamente a un método de prueba.
  • Permite múltiples métodos de prueba (cada uno con sus propios datos) por clase de prueba.
  • Admite fuentes de datos CSV, así como valores basados ​​en anotaciones (no se requiere ningún método).

Contras:

  • Requiere que el proyecto se configure con la dependencia JunitParams.
  • Al ejecutar y depurar pruebas, se deben ejecutar todas las pruebas dentro de la clase; no es posible ejecutar un solo método de prueba dentro de una clase de prueba.

Unidad 5

Pros:

  • Este marco de parametrización está integrado en JUnit 5 y mejora lo que se incluyó con JUnit 4.
  • Tiene una sintaxis de parámetros simplificada como JunitParams.
  • Admite varios tipos de fuentes de conjuntos de datos, incluidos CSV y anotaciones (no se requiere ningún método).
  • Aunque no se requieren dependencias adicionales, se necesita más de un .jar.

Contras:

  • Requiere Java 8 y una versión más reciente del sistema de compilación (Gradle 4.6 o Maven Surefire 2.21).
  • Es posible que aún no sea compatible con su IDE (al momento de escribir este artículo, solo Eclipse e IntelliJ admiten JUnit 5).

Ejemplo

Como ejemplo, supongamos que tenemos un método que procesa las solicitudes de préstamos para un banco. Podríamos escribir una prueba unitaria que especifique el monto de la solicitud de préstamo, el monto del pago inicial y otros valores. Luego, crearíamos afirmaciones que validen la respuesta: el préstamo puede ser aprobado o rechazado, y la respuesta puede especificar los términos del préstamo.

Por ejemplo:

Public LoanResponse requestLoan (monto flotante del préstamo, float downPayment, float availableFunds) {respuesta LoanResponse = new LoanResponse (); response.setApproved (verdadero); if (availableFunds <downPayment) {response.setApproved (false); response.setMessage ("error.insufficient.funds.for.down.payment"); respuesta de retorno; } si (pago inicial / monto del préstamo <0.1) {respuesta.conjunto aprobado (falso); response.setMessage ("error.insufficient.down.payment"); } respuesta de retorno; }

Ver Raw

prueba_parametrizada_ejemplo_1.java

Alojado por GitHub

Primero, veamos una prueba regular para el método anterior:

@Test public void testRequestLoan () throws Throwable {// Dado LoanProcessor underTest = new LoanProcessor (); // Cuando LoanResponse result = underTest.requestLoan (1000f, 200f, 250f); // Entonces assertNotNull (resultado); asertTrue (resultado.isAprobado ()); asertNull (result.getMessage ()); }

Ver Raw

prueba_parametrizada_ejemplo_2.java

Alojado por GitHub

En este ejemplo, estamos probando nuestro método solicitando un préstamo de $ 1000, con un pago inicial de $ 200 e indicando que el solicitante tiene $ 250 en fondos disponibles. Luego, la prueba valida que el préstamo fue aprobado y no proporcionó un mensaje en la respuesta.

Para asegurarnos de que nuestro pedir prestamo() El método se prueba a fondo, necesitamos probar con una variedad de pagos iniciales, montos de préstamos solicitados y fondos disponibles. Por ejemplo, probemos una solicitud de préstamo de $ 1 millón sin pago inicial, que debería rechazarse. Podríamos simplemente duplicar la prueba existente con diferentes valores, pero dado que la lógica de la prueba sería la misma, es más eficiente parametrizar la prueba en su lugar.

Parametrizaremos el monto del préstamo solicitado, el anticipo y los fondos disponibles, así como los resultados esperados: si el préstamo fue aprobado y el mensaje devuelto después de la validación. Cada conjunto de datos de solicitud, junto con sus resultados esperados, se convertirá en su propio caso de prueba.

Un ejemplo de prueba parametrizada usando JUnit 4 parametrizado

Comencemos con un ejemplo parametrizado de Junit 4. Para crear una prueba parametrizada, primero necesitamos definir las variables para la prueba. También necesitamos incluir un constructor para inicializarlos:

@RunWith (Parameterized.class) clase pública LoanProcessorParameterizedTest {Float Loanmount; float downPayment; float availableFunds; boolean waitApproved; String esperadoMessage; LoanProcessorParameterizedTest público (flotar el importe del préstamo, flotar abajoPayment, flotar los fondos disponibles, booleano esperarAprobar, String esperadoMessage) {this.loanAmount = préstamoAmount; this.downPayment = downPayment; this.availableFunds = availableFunds; this.expectApproved = waitApproved; this.expectedMessage = esperadoMessage; } // ...}

Ver Raw

prueba_parametrizada_ejemplo_3.java

Alojado por GitHub

Aquí, vemos que la prueba usa el @Corre con anotación para especificar que la prueba se ejecutará con el corredor parametrizado Junit4. Este corredor sabe buscar un método que proporcione el valor establecido para la prueba (anotado con @Parámetros), inicialice la prueba correctamente y ejecute las pruebas con varias filas.

Tenga en cuenta que cada parámetro se define como un campo en la clase de prueba, y el constructor inicializa estos valores (también puede inyectar valores en los campos usando el @Parámetro anotación si no desea crear un constructor). Para cada fila del conjunto de valores, el Parametrizado runner creará una instancia de la clase de prueba y ejecutará cada prueba en la clase.

Agreguemos un método que proporcione los parámetros a la Parametrizado corredor:

@Parameters (name = "Ejecutar {index}: préstamoAmount = {0}, downPayment = {1}, availableFunds = {2}, esperaApproved = {3}, esperabaMessage = {4}") public static Iterable data () lanza Throwable {return Arrays.asList (new Object [] [] {{1000.0f, 200.0f, 250.0f, true, null}}); }

Ver Raw

prueba_parametrizada_ejemplo_4.java

Alojado por GitHub

Los conjuntos de valores se construyen como Lista of Objeto matrices por el datos() método, que está anotado con @Parámetros. Tenga en cuenta que @Parámetros establece el nombre de la prueba mediante marcadores de posición, que se reemplazarán cuando se ejecute la prueba. Esto hace que sea más fácil ver los valores en los resultados de las pruebas, como veremos más adelante. Actualmente, solo hay una fila de datos, probando un caso en el que se debería aprobar el préstamo. Podemos agregar más filas para aumentar la cobertura del método bajo prueba.

@Parameters (name = "Ejecutar {index}: préstamoAmount = {0}, downPayment = {1}, availableFunds = {2}, esperaApproved = {3}, esperabaMessage = {4}") público estático Iterable data () arroja Throwable {return Arrays.asList (new Object [] [] {{1000.0f, 200.0f, 250.0f, true, null}, {1000.0f, 50.0f, 250.0f, false, "error.insufficient. down.payment "}, {1000.0f, 200.0f, 150.0f, false," error.insufficient.funds.for.down.payment "}}); }

Ver Raw

prueba_parametrizada_ejemplo_5.java

Alojado por GitHub

Aquí, tenemos un caso de prueba en el que se aprobaría el préstamo y dos casos en los que no debería aprobarse por diferentes razones. Es posible que deseemos agregar filas en las que se utilicen valores cero o negativos, así como probar las condiciones de contorno.

Ahora estamos listos para crear el método de prueba:

@Test public void testRequestLoan () throws Throwable {// Dado LoanProcessor underTest = new LoanProcessor (); // Cuando LoanResponse result = underTest.requestLoan (préstamoAmount, downPayment, availableFunds); // Entonces assertNotNull (resultado); aseverarEquals (esperarAprobado, resultado.isAprobado ()); asertEquals (mensaje esperado, resultado.getMessage ()); }

Ver Raw

prueba_parametrizada_ejemplo_6.java

Alojado por GitHub

Aquí, hacemos referencia a los campos al invocar el pedir prestamo() método y validando los resultados.

Ejemplo de JunitParams

La biblioteca JunitParams simplifica la sintaxis de prueba parametrizada al permitir que los parámetros se pasen directamente al método de prueba. Los valores de los parámetros son proporcionados por un método separado cuyo nombre se hace referencia en el @Parámetros anotación.

@RunWith (JUnitParamsRunner.class) public class LoanProcessorParameterizedTest2 {@Test @Parameters (method = "testRequestLoan_Parameters") public void testRequestLoan (float loanAmount, float downPayment, float availableFunds, booleano esperanAprobado) ThrowupessageWarnings esperados ... ("unused") objeto estático privado [] [] testRequestLoan_Parameters () arroja Throwable {// Parámetros: préstamoAmount = {0}, downPayment = {1}, availableFunds = {2}, esperaAprobado = {3}, esperabaMessage = {4 } return new Object [] [] {{1000.0f, 200.0f, 250.0f, true, null}, {1000.0f, 50.0f, 250.0f, false, "error.insufficient.down.payment"}, {1000.0f , 200.0f, 150.0f, false, "error.insufficient.funds.for.down.payment"}}; }}

Ver Raw

prueba_parametrizada_ejemplo_7.java

Alojado por GitHub

JunitParams tiene el beneficio adicional de que admite el uso de archivos CSV para proporcionar valores además de proporcionar los valores en el código. Esto permite desacoplar la prueba de los datos y actualizar los valores de los datos sin actualizar el código.

Ejemplo de Junit 5

JUnit 5 aborda algunas de las limitaciones y deficiencias de JUnit 4. Al igual que JunitParams, Junit 5 también simplifica la sintaxis de las pruebas parametrizadas. Los cambios más importantes en la sintaxis son:

  • El método de prueba está anotado con @PruebaParametrizada en lugar de @Prueba
  • El método de prueba acepta parámetros directamente, en lugar de utilizar campos y un constructor.
  • La @Corre con la anotación ya no es necesaria

Definir el mismo ejemplo en Junit 5 se vería así:

LoanProcessorParameterizedTest de clase pública {@ParameterizedTest (name = "Ejecutar {index}: préstamoAmount = {0}, downPayment = {1}, availableFunds = {2}, esperaApproved = {3}, esperabaMessage = {4}") @MethodSource (" testRequestLoan_Parameters ") public void testRequestLoan (flotar préstamoAmount, float downPayment, float availableFunds, boolean esperanAprobado, String esperadoMessage) lanza Throwable {...} flujo estático testRequestLoan_Parameters () arroja Throwable {return Stream.of (Arguments.of (1000.0f, 200.0f, 250.0f, true, null), Arguments.of (1000.0f, 50.0f, 250.0f, false, "error.insufficient.down .payment "), Arguments.of (1000.0f, 200.0f, 150.0f, false," error.insufficient.funds.for.downpayment ")); }}

Ver Raw

prueba_parametrizada_ejemplo_8.java

Alojado por GitHub

Cree pruebas parametrizadas de manera eficiente

Como se puede imaginar, escribir la prueba parametrizada anteriormente puede ser un poco complicado. Para cada marco de prueba parametrizado, hay un poco de código repetitivo que debe escribirse correctamente. Puede ser difícil recordar la estructura correcta y se necesita tiempo para escribirla. Para hacerlo mucho más fácil, puede utilizar Prueba J de Parasoft para generar pruebas parametrizadas, de forma automática, como las descritas anteriormente. Para hacer esto, simplemente seleccione el método para el que desea generar una prueba (en Eclipse o IntelliJ):

La prueba se genera utilizando valores predeterminados y afirmaciones. Luego, puede configurar la prueba con valores de entrada reales y afirmaciones, y agregar más filas de datos al método data ().

Ejecución de la prueba parametrizada

Parasoft Jtest puede ejecutar pruebas parametrizadas directamente tanto en Eclipse como en IntelliJ.

La vista JUnit en Eclipse

Tenga en cuenta que el nombre de cada prueba, como se muestra, incluye valores de entrada del conjunto de datos y valores de resultados esperados. Esto puede hacer que la depuración de la prueba sea mucho más fácil cuando falla, ya que los parámetros de entrada y las salidas esperadas se muestran para cada caso.

También puede utilizar la acción Ejecutar todo de Parasoft Jtest:

La vista de árbol de flujo en Parasoft Jtest

Analiza el flujo de prueba y proporciona información detallada sobre la ejecución de prueba anterior. Esto le permite ver lo que sucedió en la prueba sin necesidad de volver a ejecutar la prueba con puntos de interrupción o declaraciones de depuración. Por ejemplo, puede ver valores parametrizados en la vista Variables:

La vista de variables en Parasoft Jtest

Conclusión

Cada uno de los tres marcos que revisamos es una buena elección y funciona bien. Si uso JUnit 4, prefiero JunitParams sobre el marco parametrizado JUnit 4 incorporado, debido al diseño más limpio de las clases de prueba y la capacidad de definir múltiples métodos de prueba en la misma clase. Sin embargo, si usa JUnit 5, recomendaría el marco JUnit 5 incorporado, ya que soluciona las deficiencias de JUnit 4 y no requiere bibliotecas adicionales. tambien me gusta usar Pruebas unitarias de Parasoft capacidades para hacer más eficiente la creación, ejecución y depuración de pruebas parametrizadas.

Descubra cómo Parasoft Jtest puede ayudarlo a mejorar la calidad de su código Java y la productividad del equipo.