class: center, middle, inverse, title-slide .title[ # Creando paquetes de R/Bioconductor para análisis transcriptómicos de célula única ] .author[ ### Mirna Vazquez Rosas Landa
10 de agosto de 2023 ] --- # Pruebas <img src="data:image/png;base64,#css/test-rpackages.png" width="80%" style="display: block; margin: auto;" /> --- # 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**. --- class: chapter-slide # ¿Por qué vale la pena el esfuerzo de realizar pruebas? --- 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. --- 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. --- class: chapter-slide # Beneficios --- 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. --- 4. **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. 5. **Acción Inmediata:** Convertir errores en pruebas ayuda a definir objetivos concretos. Esto se alinea con la metodología de desarrollo guiado por pruebas. 6. **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. --- class: chapter-slide # [**testthat**](https://testthat.r-lib.org/index.html) --- # Instalar testthat ```r usethis::use_testthat(3) ``` Okay... ¿qué pasó después de correr este comando? --- # Crear una prueba Primero, vamos a crear una función simple. ```r calculate_mean <- function(data) { mean(data) } ``` Ahora, procedamos a crear una prueba. ```r usethis::use_test("calculate_mean") ``` ¿Qué sucedió? --- class: chapter-slide # Corramos el test --- ```r testthat::test_file("tests/testthat/test-calculate_mean.R") ``` o ```r devtools::test() ``` o ```r devtools::check() ``` --- class: chapter-slide # "Expectations" --- 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. --- class: chapter-slide # [Expectations](https://testthat.r-lib.org/reference/) --- # Igualdad ```r expect_equal(10, 10) expect_equal(10, 10L) expect_equal(10, 10 + 1e-7) expect_equal(10, 11) ``` equal e identical ```r expect_equal(10, 10 + 1e-7) expect_identical(10, 10 + 1e-7) expect_equal(2, 2L) expect_identical(2, 2L) ``` --- Vamos a reemplazar el contenido con una prueba para nuestra función de ejemplo. ```r 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) }) ``` --- # Ejecuta la prueba y observa qué sucede. --- class: chapter-slide # Probando errores --- 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? --- ```r 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 ```r expect_no_error(1 / 2) ``` --- # Modifiquemos la funcion ```r 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) } ``` --- # Hagamos un test de error ```r 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") }) ``` --- # Crea una prueba utilizando **expect_warning**, otra utilizando **expect_no_error**, y si tienes tiempo, también puedes crear una utilizando **expect_message**. --- .pull-left[<br><br><br><br><br><br><br> .center[ # ¡Gracias! ] ] .pull-right[<br> <img src="data:image/png;base64,#css/xolo.png" width="80%" style="display: block; margin: auto;" /> ]