+ - 0:00:00
Notes for current slide
Notes for next slide

Creando paquetes de R/Bioconductor para análisis transcriptómicos de célula única

Mirna Vazquez Rosas Landa
10 de agosto de 2023

1 / 27

Pruebas

2 / 27

Pruebas

La prueba es una parte fundamental del desarrollo de paquetes: garantiza que tu código haga lo que deseas.

Para hacer esta tarea más sencilla y efectiva, aqui veremos cómo realizar pruebas automatizadas formales utilizando el paquete testthat.

3 / 27

¿Por qué vale la pena el esfuerzo de realizar pruebas?

4 / 27

Hasta ahora, es probable que tu flujo de trabajo se vea así:

  1. Escribes una función.
  2. Cargas la función con devtools::load_all()
  3. Experimentas con ella en la consola para ver si funciona.
  4. Repites este proceso.

Aunque pruebas tu código de manera informal en este flujo de trabajo, esto podría olvidarse cuando vuelvas meses después para añadir nuevas características. Esto hace que sea fácil romper código que funcionaba previamente.

5 / 27

Muchos optan por las pruebas automatizadas al darse cuenta de que arreglan errores repetidamente. Aunque probamos interactuando con el código mientras lo escribimos o corregimos, es sencillo pasar por alto casos de uso importantes sin un sistema para almacenar y reproducir las pruebas. Esto es común entre los programadores de R. No se trata de no probar el código, sino de no automatizar las pruebas.

6 / 27

Beneficios

7 / 27
  1. Menos Errores: Ser explícito en el comportamiento del código reduce errores. Comparar el comportamiento en el código y las pruebas ayuda a identificar discrepancias.

  2. Pruebas Más Rigurosas: Las pruebas informales pueden omitir casos inesperados. Las pruebas formales anticipan entradas inesperadas, fortaleciendo el código.

  3. Buena Estructura de Código: Un diseño de código sólido es fácil de probar. Si las pruebas son difíciles, considera si el diseño del código es el problema.

8 / 27
  1. Refactorización Guiada: Las pruebas proporcionan retroalimentación valiosa sobre la calidad del código. Integrar pruebas en el flujo de desarrollo motiva a descomponer operaciones complejas en funciones más manejables.

  2. Acción Inmediata: Convertir errores en pruebas ayuda a definir objetivos concretos. Esto se alinea con la metodología de desarrollo guiado por pruebas.

  3. Código Robusto: Las pruebas sólidas permiten cambios seguros sin temor a romper el código. Ayudan a prevenir simplificaciones que podrían omitir casos importantes.

9 / 27

testthat

10 / 27

Instalar testthat

usethis::use_testthat(3)

Okay... ¿qué pasó después de correr este comando?

11 / 27

Crear una prueba

Primero, vamos a crear una función simple.

calculate_mean <- function(data) {
mean(data)
}

Ahora, procedamos a crear una prueba.

usethis::use_test("calculate_mean")

¿Qué sucedió?

12 / 27

Corramos el test

13 / 27
testthat::test_file("tests/testthat/test-calculate_mean.R")

o

devtools::test()

o

devtools::check()
14 / 27

"Expectations"

15 / 27

Una expectativa es el nivel más fino de pruebas. Hace una afirmación binaria sobre si un objeto tiene o no las propiedades que esperas. Este objeto suele ser el valor de retorno de una función en tu paquete.

Todas las expectativas tienen una estructura similar:

Comienzan con expect_.

Tienen dos argumentos principales: el primero es el resultado real y el segundo es lo que esperas.

Si los resultados reales y esperados no concuerdan, testthat genera un error.

Algunas expectativas tienen argumentos adicionales que controlan los puntos más finos de la comparación entre un resultado real y esperado.

16 / 27

Igualdad

expect_equal(10, 10)
expect_equal(10, 10L)
expect_equal(10, 10 + 1e-7)
expect_equal(10, 11)

equal e identical

expect_equal(10, 10 + 1e-7)
expect_identical(10, 10 + 1e-7)
expect_equal(2, 2L)
expect_identical(2, 2L)
18 / 27

Vamos a reemplazar el contenido con una prueba para nuestra función de ejemplo.

test_that("la función calcula el promedio correctamente", {
# Datos de ejemplo
data <- c(2, 4, 6, 8, 10)
# Resultado esperado
expected <- mean(data)
# Resultado real
actual <- calculate_mean(data)
# Verificar si el promedio calculado coincide con el promedio esperado
expect_equal(actual, expected)
})
19 / 27

Ejecuta la prueba y observa qué sucede.

20 / 27

Probando errores

21 / 27

Utiliza expect_error() para verificar si una expresión genera un error. Es la expectativa más importante en un trío que incluye también expect_warning() y expect_message(). Vamos a enfocarnos en los errores aquí, pero la mayoría de esto también se aplica a las advertencias y mensajes.

Por lo general, te importan dos cosas al probar un error:

  1. ¿El código falla? Específicamente, ¿falla por la razón correcta?
  2. ¿El mensaje acompañante tiene sentido para la persona que necesita manejar el error?
22 / 27
1 / "a"
#> Error in 1/"a": non-numeric argument to binary operator
expect_error(1 / "a")
log(-1)
#> Warning in log(-1): NaNs produced
#> [1] NaN
expect_warning(log(-1))

Y tambien existe la forma positiva

expect_no_error(1 / 2)
23 / 27

Modifiquemos la funcion

calculate_mean <- function(data, generar_advertencia = FALSE) {
if (!all(is.numeric(data))) {
if (generar_advertencia) {
warning("el argumento no es numérico ni lógico")
return(NA)
} else {
stop("el argumento no es numérico ni lógico")
}
}
mean(data)
}
24 / 27

Hagamos un test de error

test_that("calculate_mean() maneja errores", {
# Caso de prueba con datos no numéricos
expect_error(calculate_mean("datos_no_numericos"),
"el argumento no es numérico ni lógico")
})
25 / 27

Crea una prueba utilizando expect_warning, otra utilizando expect_no_error, y si tienes tiempo, también puedes crear una utilizando expect_message.

26 / 27








¡Gracias!


27 / 27

Pruebas

2 / 27
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow