Pruebas unitarias en programación R

La prueba unitaria básicamente son pequeñas funciones que prueban y ayudan a escribir código robusto. De un código robusto nos referimos a un código que no se romperá fácilmente con los cambios, que se puede refactorizar de forma sencilla, que se puede ampliar sin romper el resto y que se puede probar con facilidad. Las pruebas unitarias son de gran utilidad cuando se trata de lenguajes de script escritos dinámicamente, ya que no hay asistencia de un compilador que le muestre los lugares donde se pueden llamar funciones con argumentos no válidos.

¿Cómo funcionan las pruebas unitarias?

Unit-Testing-01

¿Qué hace una función simple? Toma una entrada x y devuelve una salida y. En las pruebas unitarias, verificamos que la precisión y corrección de la función devuelva el valor esperado de y para un valor específico de x al llamar a la función. Generalmente, se prueban diferentes pares (x, y). ¿Qué sucede si nuestro código tiene efectos secundarios, por ejemplo, lectura/escritura de archivos, acceso a alguna base de datos, etc.? Entonces, ¿cómo funciona una prueba unitaria? En tales escenarios, la preparación de la prueba es más compleja. Podría comprender solo un montón de objetos simulados de funciones para simular el acceso a una base de datos. Eso está influyendo en el estilo de programación para el que pueden ser necesarias las capas de abstracción. En algunos casos, los archivos de entrada deben generarse antes de ejecutar la prueba y los archivos de salida deben verificarse después de la prueba. La idea básica detrás de las pruebas unitarias es simple, escribe un script que evalúa automáticamente partes de su código y lo compara con el comportamiento esperado. Ahora veamos algunos ejemplos para una mejor comprensión de lo que realmente significan las pruebas unitarias y cómo funcionan.

Implementación en R

En la prueba de programación R, ese paquete nos ayuda a implementar pruebas unitarias en nuestros códigos. Para instalar el paquete testthat , solo necesita ejecutar el siguiente código en su consola R.

if (!require(testthat)) install.packages('testthat')

prueba que usa la función para crear una prueba y usa las expectativas para la prueba unitaria del código. Una expectativa nos permite afirmar que los valores devueltos por una función coinciden con los que deberíamos obtener. 

test_that("Message to be displayed",
          { expect_equal(function_f(input x), expected_output_y)
            expect_equivalent(function_f(input x), expected_output_y)
            expect_identical(function_f(input x), expected_output_y)
            .
            .
            .
          })

Hay más de 20 expectativas en el paquete testthat .

Argumento

Expectativas

expect_lt(), expect_lte(), expect_gt(), expect_gte() ¿El valor devuelto es menor o mayor que el valor especificado?
esperar_igual(), esperar_idéntico() ¿Es un objeto igual a un valor de referencia?

esperar_error(), esperar_advertencia(), esperar_mensaje(), 

expect_condition()

¿El código genera un error, una advertencia, un mensaje u otra condición?
esperar_invisible(), esperar_visible() ¿La expresión vuelve visible o invisible?

skip(), skip_if_not(), skip_if(), skip_if_not_installed(), skip_if_offline(), skip_on_cran(), skip_on_os(), skip_on_travis(), skip_on_appveyor(), 

skip_on_ci(), skip_on_covr(), skip_on_bioc(), skip_if_translated()

Saltar una prueba.
esperar_longitud() ¿Tiene un vector la longitud especificada?
esperar_coincidencia() ¿La string coincide con una expresión regular?
expect_named() ¿El objeto tiene nombres?
expect_setequal(), expect_mapequal() ¿Dos vectores contienen los mismos valores?
expect_output() ¿El código imprime la salida en la consola?
esperar_referencia() ¿Dos nombres apuntan al mismo objeto subyacente?

expect_snapshot_output(), expect_snapshot_value(),

expect_snapshot_error(), expect_snapshot_condition()

Prueba de instantáneas.
esperar_vector() ¿El objeto tiene propiedades vectoriales?
expect_silent() ¿El código es silencioso?
expect_type(), expect_s3_class(), expect_s4_class() ¿El objeto hereda de una clase S3 o S4, o es un tipo base?
esperar_verdadero(), esperar_falso() ¿El objeto es verdadero/falso?
verificar_salida() Verificar salida

Ejemplo: Defina un factorial de función que tome un valor numérico n y devuelva su factorial.

R

# create a recursive program that 
# calculates the factorial of number n
factorial <- function(n)
{
  if(n == 0)
  {
    return(1)
  }
  else
  {
    return(n * factorial(n - 2))
  }
}

Ahora, realicemos pruebas unitarias en nuestra función factorial y probemos su precisión y depuremos nuestro programa.

R

# import testthat package
library(testthat)
  
# use expect_that to create tests
expect_that
(
  "Factorial of number $n",
  {
    expect_equal(factorial(5), 120)
    expect_identical(factorial(2), 2)
    expect_equal(factorial(8), 40320)
  }
)

Producción:

Error: Test failed: 'Factorial computed correctly'
* factorial(5) not equal to 120.
1/1 mismatches
[1] 15 - 120 == -105
* factorial(8) not equal to 40320.
1/1 mismatches
[1] 384 - 40320 == -39936

La prueba da error, significa que nuestra función no devuelve los resultados deseados. A sabiendas escribimos mal nuestro código. En la función factorial, el enfoque recursivo que usamos tiene un error. Erradiquemos ese error y probemos nuestra función una vez más.

R

# create a recursive program that
# calculates the factorial of number n
factorial <- function(n)
{
  if(n == 0)
  {
    return(1)
  }
  else
  {
    # notice we used (n-2) instead
    # of (n-1) in our previous code
    return(n * factorial(n - 1))
  }
}
  
# import testthat package
library(testthat)
  
# use expect_that to create tests
expect_that
(
  "Factorial of number $n",
  {
    expect_equal(factorial(5), 120)
    expect_identical(factorial(2), 2)
    expect_equal(factorial(8), 40320)
  }
)
# no error

Nota: si su código fuente y paquetes no están en el mismo directorio, debe ejecutar una línea de código con función para ejecutar pruebas.

source("your_file_path")  # This is only needed if your project is not a package

Es muy importante organizar sus archivos y pruebas. Deberíamos tener una carpeta llamada R con todos los archivos de código R, y una carpeta llamada tests/testthat, donde vivirán todos los scripts de prueba. Desde la consola R, puede ejecutar todas las pruebas en un archivo con 

test_file("./path/to/file")

Y todas las pruebas en una carpeta con

test_dir("./path/to/folder")

Ambas funciones mencionadas anteriormente aceptan un reportero de parámetros especial que tiene varias opciones para ofrecer, que son

  • progreso  es el valor por defecto
  • mínimo   para un informe mínimo
  • la ubicación    muestra el archivo, la línea y la columna de la ejecución de la prueba (fallida o no),
  • debug      le permite depurar de forma interactiva una prueba fallida y más.
test_dir("./path/to/folder", reporter=c("minimal", "location"))

Las pruebas unitarias son necesarias si desea un código bien formado y libre de errores.

Publicación traducida automáticamente

Artículo escrito por misraaakash1998 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *