SlideShare una empresa de Scribd logo
1 de 49
Descargar para leer sin conexión
Testeo y depuración en R
Primera línea de defensa contra los bugs
Virginia Peón García
Pixabay
Nunca me encontrarán
escondido en el código,
Muahahaha
● Depurando (print)
Temario
● Comprobación de inputs de una función (if / stop / assert)
● Manejando excepciones (try / try catch)
● Test unitarios
○ Características y ejemplos
○ Metodología TDD
Presentación a través de retos
En la exposición voy a ir presentando retos que resolver.
Cuando se alcance el resultado saldrá:
Primer reto
Crear una función, my_factorial(), que calcule el factorial de un
número.
El factorial de un entero positivo n se define como el producto de
los números enteros positivos desde 1 hasta n. Por ejemplo,
El factorial de 5 es:
1*2*3*4*5 = 120
Primera versión
my_factorial <- function(n) {
factorial <- 0
for(i in 1:n) {
factorial <- i * factorial
}
return(factorial)
}
> my_factorial(5)
[1] 0
✗
Depurando con print
my_factorial <- function(n) {
factorial <- 0
for(i in 1:n) {
factorial <- i * factorial
}
return(factorial)
}
> my_factorial(5)
[1] "1 = > 0"
[1] "2 = > 0"
[1] "3 = > 0"
[1] "4 = > 0"
[1] "5 = > 0"
[1] 0
print(paste(i, factorial, sep = " => "))
✗
Depurando con print
my_factorial <- function(n) {
factorial <- 0
for(i in 1:n) {
factorial <- i * factorial
print(paste(i, factorial, sep = " => "))
}
return(factorial)
}
factorial <- 1
✓
> my_factorial(5)
[1] "1 = > 1"
[1] "2 = > 2"
[1] "3 = > 6"
[1] "4 = > 24"
[1] "5 = > 120"
[1] 120
Version depurada
my_factorial <- function(n) {
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
return(factorial)
}
> my_factorial(5)
[1] 120
✓
Comprobación de inputs
> my_factorial(5)
[1] 120
> my_factorial(0)
[1] 0
> my_factorial(-5)
[1] 0
El factorial de un entero positivo n se define como el producto de
los números enteros positivos desde 1 hasta n. Por convenio el
factorial de 0 es 1.
✓
✗
✗
Segundo reto
Modificar la función, my_factorial(), para que en caso de un número
negativo devuelva un nulo y cuando se le pase 0 la solución sea 1.
Comprobación de inputs: if
my_factorial <- function(n) {
if(n < 0) {
return(NULL)
}
if(n == 0) {
return(1)
}
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
return(factorial)
}
> my_factorial(5)
[1] 120
> my_factorial(0)
[1] 1
> my_factorial(-5)
[1] NULL
> my_factorial("a")
Error in 1:n : NA/NaN argument
In addition: Warning message:
In my_factorial("a") : NAs
introduced by coercion
✓
✓
✓
Tercer reto
Pare la función my_factorial() cuando el valor de entrada no
sea numérico.
Comprobación de inputs: stop / if / assert
my_factorial <- function(n) {
stopifnot(is.numeric(n))
if(n < 0) {
return(NULL)
}
if(n == 0) {
return(1)
}
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
return(factorial)
}
> my_factorial("a")
Error: is.numeric(n) is not TRUE
✓ > my_factorial("a")
Error: Sorry, n must be a number
✓
my_factorial <- function(n) {
if(!is.numeric(n)){stop("Sorry, n must be a number")}
if(n < 0) {
return(NULL)
}
if(n == 0) {
return(1)
}
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
return(factorial)
}
Comprobación de inputs: stop / if / assert
library(assertthat)
my_factorial <- function(n) {
assert_that(
is.numeric(n),
msg = "Sorry, n must be a number"
)
if(n < 0) {
return(NULL)
}
if(n == 0) {
return(1)
}
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
return(factorial)
}
> my_factorial("a")
Error: Sorry, n must be a number
✓ ✓
library(assertthat)
my_factorial <- function(n) {
assert_that(is.numeric(n))
if(n < 0) {
return(NULL)
}
if(n == 0) {
return(1)
}
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
return(factorial)
}
> my_factorial("a")
Error: n is not a numeric or integer vector
Cuarto reto
Crear una función, my_factorial_for_file(), que calcule el
factorial de un número que lee de un archivo csv.
Y al final imprimir: “T ' F ! ”
Manejando excepciones
library(assertthat)
my_factorial_for_file <- function(filename) {
n <- read.csv(filename, header = FALSE)[1, 1]
assert_that(is.numeric(n))
if(n < 0) {
return(NULL)
}
if(n == 0) {
return(1)
}
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
return(factorial)
print("-- That's all folks! --")
}
> my_factorial_for_file("num.csv")
[1] 120
[1] "-- That's all folks! --"
> my_factorial_for_file("num2.csv")
Error in file(file, "rt") : cannot open
the connection In addition: Warning
message:
In file(file, "rt") :
cannot open file 'num2.csv': No such
file or directory
✓
✗
5
num.csv
Manejando excepciones: Try
library(assertthat)
my_factorial_for_file <- function(filename) {
try({
n <- read.csv(filename, header = FALSE)[1, 1]
assert_that(is.numeric(n))
if(n < 0) {
return(NULL)
}
if(n == 0) {
print(1)
}
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
print(factorial)
})
print("-- That's all folks! ---")
}
> my_factorial_for_file("num.csv")
[1] 120
[1] "-- That's all folks! --"
> my_factorial_for_file("num2.csv")
Error in file(file, "rt") : cannot
open the connection
In addition: Warning message:
In file(file, "rt") :
cannot open file 'num2.csv': No
such file or directory
[1] "-- That's all folks! ---"
✓
✓
5
num.csv
Manejando excepciones: Try catch
tryCatch({
expression to be evaluated
}, warning = function(w) {
warning-handler-code
}, error = function(e) {
error-handler-code
}, finally = {
cleanup-code
}
La sintaxis del manejo de excepciones de try catch en R es
similar a la de otros lenguajes:
Manejando excepciones: Try catch
library(assertthat)
my_factorial_for_file <- function(filename) {
tryCatch({
n <- read.csv(filename, header = FALSE)[1, 1]
assert_that(is.numeric(n))
if(n < 0) {
return(NULL)
}
if(n == 0) {
print(1)
}
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
print(factorial)
}, warning = function(w) {
print("Ups, a warning:")
print(w)
}, error = function(e) {
print("There is an error:")
print(e)
}, finally = {
print("-- That's all folks! ---")
})
}
> my_factorial_for_file("num.csv")
[1] 120
[1] "-- That's all folks! --"
> my_factorial_for_file("num2.csv")
[1] "Ups, a warning:"
<simpleWarning in file(file, "rt"):
cannot open file 'num2.csv': No such
file or directory>
[1] "-- That's all folks! ---"
✓
✓
5
num.csv
Comprobaciones una y otra vez sobre lo mismo
> my_factorial(5)
[1] 120
> my_factorial(0)
[1] 1
> my_factorial(-5)
[1] NULL
Sería maravilloso que
cada vez que cambiase
el código se pudiera
evaluar de forma
sencilla
Pixabay
Test unitarios
● Solo deben fallar cuando se introduce un bug
● Los test deben acabar rápidamente, tardar menos de un minuto en ejecutarse
● Debe ser fácil detectar dónde se produce el error para poderlo solucionar
Un test unitario es una secuencia de comandos que evalúa la unidad más pequeña del código
y lo compara con el comportamiento esperado.
Deberían:
● Los test deben ser independientes unos de otros
Una buena nomenclatura
Código que
queremos
testear
foo.R
Código de
testeo
tests/test.foo.R
¡Eh, mira, un test unitario!
library("testthat")
test_that("testing inputs", {
expect_equal(my_factorial(5), 120)
}) > test_file('tests/test.my_factorial.R')
.
DONE =================================
> test_file('tests/test.my_factorial.R')
Error: Test failed: 'testing inputs'
* my_factorial(5) not equal to 120.
1/1 mismatches
[1] 0 - 120 == -120
Si my_factorial(5) devuelve 0
Si my_factorial(5) devuelve 120
Tenemos la expectativa de que my_factorial(5)
devuelva 120
¡Eh, mira, un test unitario!
library("testthat")
test_that("testing inputs", {
expect_equal(my_factorial(5), 120)
expect_equal(my_factorial(0), 1)
expect_equal(my_factorial(-1), NULL)
})
> test_file('tests/test.my_factorial.R')
...
DONE ==================================================================
"testthat: Get Started with Testing" Hadley Wickham
Expectation
Una expectation es una afirmación binaria sobre si un valor es o no lo que se
espera. Sólo en caso de no ser verdadera dará un error.
Hay 11 tipos:
expect_that(x, is_true())
expect_that(x, is_false())
expect_that(x, is_a(y))
expect_that(x, equals(y))
expect_that(x, is_equivalent_to(y))
expect_that(x, is_identical_to(y))
expect_that(x, matches(y))
expect_that(x, prints_text(y))
expect_that(x, shows_message(y))
expect_that(x, gives_warning(y))
expect_that(x, throws_error(y))
expect_true(x)
expect_false(x)
expect_is(x, y)
expect_equal(x, y)
expect_equivalent(x, y)
expect_identical(x, y)
expect_matches(x, y)
expect_output(x, y)
expect_message(x, y)
expect_warning(x, y)
expect_error(x, y)
Full Short curt
Ejemplos de expectations
library("testthat")
test_that("example of expectations", {
expect_equal(1, 1) # pass
expect_equal(1, 1 + 1e-8) # pass
expect_equal(1, 5) # fail
expect_identical(1, 1) # pass
expect_identical(1, 1 + 1e-8) # fail
expect_identical(3, 1 + 2) # pass
expect_identical(0.3, 0.1 + 0.2) # fail
})
> test_file('tests/test.examples.R')
..1.2.3
Failed
---------------------------------------------
1. Failure: example of
expectations(@test.examples.R#6) ------------
1 not equal to 5.
1/1 mismatches
[1] 1 - 5 == -4
2. Failure: example of expectations
(@test.examples.R#8) ------------------------
1 not identical to 1 + 1e-08.
Objects equal but not identical
3. Failure: example of expectations
(@test.examples.R#10) -----------------------
0.3 not identical to 0.1 + 0.2.
Objects equal but not identical
DONE ========================================
1
2
3
4
5
6
7
8
9
10
11
Ejemplos de expectations
library("testthat")
context("testing expectations")
test_that("testing expect_true", {
expect_true(TRUE) # pass
expect_true(FALSE) # fail
})
test_that("testing expect_is", {
model <- lm(c(6:10) ~ c(1:5))
expect_is(model, "lm") # pass
expect_is(model, "numeric") # fail
})
> test_file('tests/test.examples.R')
testing expectations: .1.2
Failed
------------------------------------------------------
1. Failure: testing expect_true (@test.examples2.R#7)
---------
FALSE isn't true.
2. Failure: testing expect_is (@test.examples2.R#13)
---------
`model` inherits from `lm` not `numeric`.
DONE
======================================================
1
2
3
4
5
6
7
8
9
10
11
12
13
14
asserthat::with_mock()
with_mock() ejecuta el código después de sustituir temporalmente las implementaciones de
las funciones del paquete.
Podemos tener en nuestro código funciones que sean difícil de testear.
● Llamadas a un servicio que no está disponible
● Resultados impredecibles al llamar a una función
● Un código que tarde demasiado en ejecutarse
¿Cómo testear en estos casos que el código es el correcto?
Un expectation con with_mock()
library(assertthat)
my_factorial_for_file <- function(filename) {
tryCatch({
n <- read.csv(filename, header = FALSE)[1, 1]
assert_that(is.numeric(n))
if(n < 0) {
return(NULL)
}
if(n == 0) {
print(1)
}
factorial <- 1
for(i in 1:n) {
factorial <- i * factorial
}
print(factorial)
}, warning = function(w) {
print("Ups, a warning:")
print(w)
}, error = function(e) {
print("There is an error:")
print(e)
}, finally = {
# print("-- That's all folks! ---")
})
}
library("testthat")
context("testing my_factorial_for_file()")
test_that("testing inputs", {
with_mock(
read.csv = function(filename, header = FALSE) {data.frame(5)},
{expect_equal(my_factorial_for_file(5), 120)}
)
})
> test_file('tests/test.my_factorial_for_file.R')
testing my_factorial_for_file(): [1] 120
.
DONE
=======================================================================
✓
Varios expectations con with_mock()
library(assertthat)
my_factorial_for_file <- function(filename) {
tryCatch({
n <- read.csv(filename, header = FALSE)[1, 1]
assert_that(is.numeric(n))
factorial <- 1
if(n < 0) {
return(NULL)
}
if(n == 0) {
print(1)
}
for(i in 1:n) {
factorial <- i * factorial
}
print(factorial)
}, warning = function(w) {
print("Ups, a warning:")
print(w)
}, error = function(e) {
print("There is an error:")
print(e)
}, finally = {
# print("-- That's all folks! ---")
})
}
library("testthat")
context("testing my_factorial_for_file()")
test_that("testing inputs", {
with_mock(
read.csv = function(filename, header = FALSE) {data.frame(5)},
{expect_equal(my_factorial_for_file(5), 120)}
)
with_mock(
read.csv = function(filename, header = FALSE) {data.frame(1)},
{expect_equal(my_factorial_for_file(0), 1)}
)
})
> test_file('tests/test.my_factorial_for_file.R')
testing my_factorial_for_file(): [1] 120
.[1] 1
.
DONE
=======================================================================
✓
Último reto
Crear una función, add_one(), que sume uno al número que se le pase.
En caso de que no se le pase un número debe devolver NULL.
Utilizar la metodología Test-Driven Development (TDD).
Workflow (basado en Test-driven development)
Escribe el test
tests/test.foo.R
Escribe el código
mínimo
foo.R
¿Pasa
el
test?
No
Pixabay
 ¿escribir el test antes que el código?
¿Me estás tomando el pelo?
 Por qué escribir el test antes que el código
● Nos aseguramos de obtener el resultado deseado y de que
efectivamente vamos a generar código testeable
● Sirve de guía si no sabes por dónde empezar pero sabes lo que
quieres obtener
● Seamos realistas, lo más seguro es que una vez que el código
realiza lo que queremos nos dará más pereza escribir el test
Test unitarios y paquetes: Test workflow
● Crear un paquete
● Crear test para una función, add_one(), que sume una unidad al número que
se le pase de argumento y en caso de no pasarle un número nos devuelva un
texto
● Crear la función, add_one()
● Estudiar la cobertura de test del paquete
Extractos de "Package Development
with devtools Cheat Sheet"
context("")
test_that("", {
expect_equal()
})
Test unitarios y paquetes: Test workflow (anotaciones)
Plantilla de test unitario:
Test unitarios y paquetes: Test workflow (anotaciones)
File –> New Project -> New Directory -> R Package
devtools::use_testthat() crear carpeta test/testthat
Revisar DESCRIPTION
Crear test.add_one.R
context("test add_one()")
test_that("test add one", {
expect_equal(add_one(5), 6)
expect_equal(add_one(0.5), 1.5)
expect_equal(add_one(-42), -41)
})
test_that("test is not numeric", {
expect_equal(add_one('a'), NULL)
})
Test unitarios y paquetes: Test workflow (anotaciones)
Crear add_one.R
devtools::test()
# add_one.R
add_one <- function(x){
if(!is.numeric(x)){
return(NULL)
}
return(x + 1)
}
library(covr)
cov <- package_coverage()
cov
report(cov)
Test unitarios y paquetes
Unit tests
Pixabay
Devrant
¡Cuidado! El código puede pasar todos los test unitarios y no funcionar por
problemas de integración
Enlaces de interés
● https://journal.r-project.org/archive/2011-1/RJournal_2011-1_Wickham.pdf
● http://courses.had.co.nz/11-devtools/slides/7-testing.pdf
● https://es.slideshare.net/mobile/egoodwintx/unit-testing-in-r-with-testthat-hrug
● https://b-rodrigues.github.io/fput/unit-testing.html#introduction
● https://katherinemwood.github.io/post/testthat/
● http://kbroman.org/Tools4RR/assets/lectures/09_testdebug_withnotes.pdf
● http://sd.jtimothyking.com/2006/07/11/twelve-benefits-of-writing-unit-tests-first/
● https://www.is.uni-freiburg.de/ressourcen/algorithm-design-and-software-engineering-oeffentlicher-zugri
ff/11_softwaretesting.pdf
● https://www.rstudio.com/wp-content/uploads/2015/03/devtools-cheatsheet.pdf
● http://r-pkgs.had.co.nz/tests.html
● http://www.machinegurning.com/rstats/test-driven-development/
● http://stat545.com/packages05_foofactors-package-02.html
Dudas y preguntas
Unit tests
Extra: traceback()
> g <- function(x) x + "a"
> g(0)
Error in x + "a" : non-numeric argument to binary operator
> f <- function(x) g(x)
> f(0)
Error in x + "a" : non-numeric argument to binary operator
> traceback()
2: g(x) at #1
1: f(0)

Más contenido relacionado

La actualidad más candente (6)

Ejercisos condicionales
Ejercisos condicionalesEjercisos condicionales
Ejercisos condicionales
 
comandos
comandoscomandos
comandos
 
Instrucciones(raptor, java, c#)
Instrucciones(raptor, java, c#)Instrucciones(raptor, java, c#)
Instrucciones(raptor, java, c#)
 
Interpolaion c++
Interpolaion c++Interpolaion c++
Interpolaion c++
 
Pauta Guia 1(1) Bloc De Notas
Pauta Guia 1(1)   Bloc De NotasPauta Guia 1(1)   Bloc De Notas
Pauta Guia 1(1) Bloc De Notas
 
Trabajo dehoy (1)
Trabajo dehoy (1)Trabajo dehoy (1)
Trabajo dehoy (1)
 

Similar a Testeo y depuración en R

Arreglo unidimensionales y bidimensionales
Arreglo unidimensionales y bidimensionalesArreglo unidimensionales y bidimensionales
Arreglo unidimensionales y bidimensionales
Marco Garay
 
Fundamentos De Algoritmia
Fundamentos De AlgoritmiaFundamentos De Algoritmia
Fundamentos De Algoritmia
cckokyco
 
Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?
Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?
Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?
Agile Spain
 

Similar a Testeo y depuración en R (20)

Javascript
JavascriptJavascript
Javascript
 
Charla evento TestingUY 2015 - Property-Based Testing Usando Quickcheck, o có...
Charla evento TestingUY 2015 - Property-Based Testing Usando Quickcheck, o có...Charla evento TestingUY 2015 - Property-Based Testing Usando Quickcheck, o có...
Charla evento TestingUY 2015 - Property-Based Testing Usando Quickcheck, o có...
 
Property Based Testing usando QuickCheck
Property Based Testing usando QuickCheckProperty Based Testing usando QuickCheck
Property Based Testing usando QuickCheck
 
¿En qué la estamos regando en pruebas de software?
¿En qué la estamos regando en pruebas de software?¿En qué la estamos regando en pruebas de software?
¿En qué la estamos regando en pruebas de software?
 
Arreglo unidimensionales y bidimensionales
Arreglo unidimensionales y bidimensionalesArreglo unidimensionales y bidimensionales
Arreglo unidimensionales y bidimensionales
 
Fundamentos De Algoritmia
Fundamentos De AlgoritmiaFundamentos De Algoritmia
Fundamentos De Algoritmia
 
Intro_Matlab_1.pdf
Intro_Matlab_1.pdfIntro_Matlab_1.pdf
Intro_Matlab_1.pdf
 
Php
PhpPhp
Php
 
Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?
Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?
Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?
 
Clase 6
Clase 6Clase 6
Clase 6
 
Tipos basicos
Tipos basicosTipos basicos
Tipos basicos
 
Machine Learning con Tensorflow y R, presentado por RStudio
Machine Learning con Tensorflow y R, presentado por RStudioMachine Learning con Tensorflow y R, presentado por RStudio
Machine Learning con Tensorflow y R, presentado por RStudio
 
Factorial en C++
Factorial en C++Factorial en C++
Factorial en C++
 
32773 php-basico
32773 php-basico32773 php-basico
32773 php-basico
 
Ruby
RubyRuby
Ruby
 
Elementos del Hardware y Software
Elementos del Hardware y SoftwareElementos del Hardware y Software
Elementos del Hardware y Software
 
C curso intr
C curso intr C curso intr
C curso intr
 
Clase 7
Clase 7Clase 7
Clase 7
 
Curso TDD Ruby on Rails #03: Tests unitarios
Curso TDD Ruby on Rails #03: Tests unitariosCurso TDD Ruby on Rails #03: Tests unitarios
Curso TDD Ruby on Rails #03: Tests unitarios
 
Subporgramacion
SubporgramacionSubporgramacion
Subporgramacion
 

Último

Chat GPT para la educación Latinoamerica
Chat GPT para la educación LatinoamericaChat GPT para la educación Latinoamerica
Chat GPT para la educación Latinoamerica
EdwinGarca59
 
editorial de informática de los sueños.docx
editorial de informática de los sueños.docxeditorial de informática de los sueños.docx
editorial de informática de los sueños.docx
ssusere34b451
 

Último (20)

el uso de las TIC en la vida cotidiana.pptx
el uso de las TIC en la vida cotidiana.pptxel uso de las TIC en la vida cotidiana.pptx
el uso de las TIC en la vida cotidiana.pptx
 
Electricidad Libro compendio de temas estudiados.docx
Electricidad Libro compendio de temas estudiados.docxElectricidad Libro compendio de temas estudiados.docx
Electricidad Libro compendio de temas estudiados.docx
 
Desarrollo del Dominio del Internet - Estrada
Desarrollo del Dominio del Internet - EstradaDesarrollo del Dominio del Internet - Estrada
Desarrollo del Dominio del Internet - Estrada
 
Chat GPT para la educación Latinoamerica
Chat GPT para la educación LatinoamericaChat GPT para la educación Latinoamerica
Chat GPT para la educación Latinoamerica
 
Tipos de datos en Microsoft Access de Base de Datos
Tipos de datos en Microsoft Access de Base de DatosTipos de datos en Microsoft Access de Base de Datos
Tipos de datos en Microsoft Access de Base de Datos
 
Inteligencia Artificial para usuarios nivel inicial
Inteligencia Artificial para usuarios nivel inicialInteligencia Artificial para usuarios nivel inicial
Inteligencia Artificial para usuarios nivel inicial
 
Navegadores de internet - Nuevas Tecnologías de la Información y la Comunicación
Navegadores de internet - Nuevas Tecnologías de la Información y la ComunicaciónNavegadores de internet - Nuevas Tecnologías de la Información y la Comunicación
Navegadores de internet - Nuevas Tecnologías de la Información y la Comunicación
 
Uso de las TIC en la vida cotidiana .
Uso de las TIC en la vida cotidiana       .Uso de las TIC en la vida cotidiana       .
Uso de las TIC en la vida cotidiana .
 
JORNADA INTELIGENCIA ARTIFICIAL Y REALIDAD VIRTUAL
JORNADA INTELIGENCIA ARTIFICIAL Y REALIDAD VIRTUALJORNADA INTELIGENCIA ARTIFICIAL Y REALIDAD VIRTUAL
JORNADA INTELIGENCIA ARTIFICIAL Y REALIDAD VIRTUAL
 
VelderrainPerez_Paola_M1C1G63-097.pptx. LAS TiC
VelderrainPerez_Paola_M1C1G63-097.pptx. LAS TiCVelderrainPerez_Paola_M1C1G63-097.pptx. LAS TiC
VelderrainPerez_Paola_M1C1G63-097.pptx. LAS TiC
 
Tipos de Datos de Microsoft Access-JOEL GARCIA.pptx
Tipos de Datos de Microsoft Access-JOEL GARCIA.pptxTipos de Datos de Microsoft Access-JOEL GARCIA.pptx
Tipos de Datos de Microsoft Access-JOEL GARCIA.pptx
 
Actividad 6/Las TIC en la Vida Cotidiana.
Actividad 6/Las TIC en la Vida Cotidiana.Actividad 6/Las TIC en la Vida Cotidiana.
Actividad 6/Las TIC en la Vida Cotidiana.
 
PRÁCTICA Nº 4: “Análisis de secuencias del ADN con el software BioEdit y uso ...
PRÁCTICA Nº 4: “Análisis de secuencias del ADN con el software BioEdit y uso ...PRÁCTICA Nº 4: “Análisis de secuencias del ADN con el software BioEdit y uso ...
PRÁCTICA Nº 4: “Análisis de secuencias del ADN con el software BioEdit y uso ...
 
innovacion banking & warehouse 2024 blog
innovacion banking & warehouse 2024 bloginnovacion banking & warehouse 2024 blog
innovacion banking & warehouse 2024 blog
 
Ejercicio 1 periodo 2 de Tecnología 2024
Ejercicio 1 periodo 2 de Tecnología 2024Ejercicio 1 periodo 2 de Tecnología 2024
Ejercicio 1 periodo 2 de Tecnología 2024
 
editorial de informática de los sueños.docx
editorial de informática de los sueños.docxeditorial de informática de los sueños.docx
editorial de informática de los sueños.docx
 
Imágenes digitales: Calidad de la información
Imágenes digitales: Calidad de la informaciónImágenes digitales: Calidad de la información
Imágenes digitales: Calidad de la información
 
BUSCADORES DE INTERNET (Universidad de Sonora).
BUSCADORES DE INTERNET (Universidad de Sonora).BUSCADORES DE INTERNET (Universidad de Sonora).
BUSCADORES DE INTERNET (Universidad de Sonora).
 
De Olmos Santiago_Dolores _ M1S3AI6.pptx
De Olmos Santiago_Dolores _ M1S3AI6.pptxDe Olmos Santiago_Dolores _ M1S3AI6.pptx
De Olmos Santiago_Dolores _ M1S3AI6.pptx
 
NIVEL DE MADUREZ TECNOLÓGICA (TRL).pptx
NIVEL DE  MADUREZ TECNOLÓGICA (TRL).pptxNIVEL DE  MADUREZ TECNOLÓGICA (TRL).pptx
NIVEL DE MADUREZ TECNOLÓGICA (TRL).pptx
 

Testeo y depuración en R

  • 1. Testeo y depuración en R Primera línea de defensa contra los bugs Virginia Peón García
  • 2. Pixabay Nunca me encontrarán escondido en el código, Muahahaha
  • 3. ● Depurando (print) Temario ● Comprobación de inputs de una función (if / stop / assert) ● Manejando excepciones (try / try catch) ● Test unitarios ○ Características y ejemplos ○ Metodología TDD
  • 4. Presentación a través de retos En la exposición voy a ir presentando retos que resolver. Cuando se alcance el resultado saldrá:
  • 5. Primer reto Crear una función, my_factorial(), que calcule el factorial de un número. El factorial de un entero positivo n se define como el producto de los números enteros positivos desde 1 hasta n. Por ejemplo, El factorial de 5 es: 1*2*3*4*5 = 120
  • 6. Primera versión my_factorial <- function(n) { factorial <- 0 for(i in 1:n) { factorial <- i * factorial } return(factorial) } > my_factorial(5) [1] 0 ✗
  • 7. Depurando con print my_factorial <- function(n) { factorial <- 0 for(i in 1:n) { factorial <- i * factorial } return(factorial) } > my_factorial(5) [1] "1 = > 0" [1] "2 = > 0" [1] "3 = > 0" [1] "4 = > 0" [1] "5 = > 0" [1] 0 print(paste(i, factorial, sep = " => ")) ✗
  • 8. Depurando con print my_factorial <- function(n) { factorial <- 0 for(i in 1:n) { factorial <- i * factorial print(paste(i, factorial, sep = " => ")) } return(factorial) } factorial <- 1 ✓ > my_factorial(5) [1] "1 = > 1" [1] "2 = > 2" [1] "3 = > 6" [1] "4 = > 24" [1] "5 = > 120" [1] 120
  • 9. Version depurada my_factorial <- function(n) { factorial <- 1 for(i in 1:n) { factorial <- i * factorial } return(factorial) } > my_factorial(5) [1] 120 ✓
  • 10. Comprobación de inputs > my_factorial(5) [1] 120 > my_factorial(0) [1] 0 > my_factorial(-5) [1] 0 El factorial de un entero positivo n se define como el producto de los números enteros positivos desde 1 hasta n. Por convenio el factorial de 0 es 1. ✓ ✗ ✗
  • 11. Segundo reto Modificar la función, my_factorial(), para que en caso de un número negativo devuelva un nulo y cuando se le pase 0 la solución sea 1.
  • 12. Comprobación de inputs: if my_factorial <- function(n) { if(n < 0) { return(NULL) } if(n == 0) { return(1) } factorial <- 1 for(i in 1:n) { factorial <- i * factorial } return(factorial) } > my_factorial(5) [1] 120 > my_factorial(0) [1] 1 > my_factorial(-5) [1] NULL > my_factorial("a") Error in 1:n : NA/NaN argument In addition: Warning message: In my_factorial("a") : NAs introduced by coercion ✓ ✓ ✓
  • 13. Tercer reto Pare la función my_factorial() cuando el valor de entrada no sea numérico.
  • 14. Comprobación de inputs: stop / if / assert my_factorial <- function(n) { stopifnot(is.numeric(n)) if(n < 0) { return(NULL) } if(n == 0) { return(1) } factorial <- 1 for(i in 1:n) { factorial <- i * factorial } return(factorial) } > my_factorial("a") Error: is.numeric(n) is not TRUE ✓ > my_factorial("a") Error: Sorry, n must be a number ✓ my_factorial <- function(n) { if(!is.numeric(n)){stop("Sorry, n must be a number")} if(n < 0) { return(NULL) } if(n == 0) { return(1) } factorial <- 1 for(i in 1:n) { factorial <- i * factorial } return(factorial) }
  • 15. Comprobación de inputs: stop / if / assert library(assertthat) my_factorial <- function(n) { assert_that( is.numeric(n), msg = "Sorry, n must be a number" ) if(n < 0) { return(NULL) } if(n == 0) { return(1) } factorial <- 1 for(i in 1:n) { factorial <- i * factorial } return(factorial) } > my_factorial("a") Error: Sorry, n must be a number ✓ ✓ library(assertthat) my_factorial <- function(n) { assert_that(is.numeric(n)) if(n < 0) { return(NULL) } if(n == 0) { return(1) } factorial <- 1 for(i in 1:n) { factorial <- i * factorial } return(factorial) } > my_factorial("a") Error: n is not a numeric or integer vector
  • 16. Cuarto reto Crear una función, my_factorial_for_file(), que calcule el factorial de un número que lee de un archivo csv. Y al final imprimir: “T ' F ! ”
  • 17. Manejando excepciones library(assertthat) my_factorial_for_file <- function(filename) { n <- read.csv(filename, header = FALSE)[1, 1] assert_that(is.numeric(n)) if(n < 0) { return(NULL) } if(n == 0) { return(1) } factorial <- 1 for(i in 1:n) { factorial <- i * factorial } return(factorial) print("-- That's all folks! --") } > my_factorial_for_file("num.csv") [1] 120 [1] "-- That's all folks! --" > my_factorial_for_file("num2.csv") Error in file(file, "rt") : cannot open the connection In addition: Warning message: In file(file, "rt") : cannot open file 'num2.csv': No such file or directory ✓ ✗ 5 num.csv
  • 18. Manejando excepciones: Try library(assertthat) my_factorial_for_file <- function(filename) { try({ n <- read.csv(filename, header = FALSE)[1, 1] assert_that(is.numeric(n)) if(n < 0) { return(NULL) } if(n == 0) { print(1) } factorial <- 1 for(i in 1:n) { factorial <- i * factorial } print(factorial) }) print("-- That's all folks! ---") } > my_factorial_for_file("num.csv") [1] 120 [1] "-- That's all folks! --" > my_factorial_for_file("num2.csv") Error in file(file, "rt") : cannot open the connection In addition: Warning message: In file(file, "rt") : cannot open file 'num2.csv': No such file or directory [1] "-- That's all folks! ---" ✓ ✓ 5 num.csv
  • 19. Manejando excepciones: Try catch tryCatch({ expression to be evaluated }, warning = function(w) { warning-handler-code }, error = function(e) { error-handler-code }, finally = { cleanup-code } La sintaxis del manejo de excepciones de try catch en R es similar a la de otros lenguajes:
  • 20. Manejando excepciones: Try catch library(assertthat) my_factorial_for_file <- function(filename) { tryCatch({ n <- read.csv(filename, header = FALSE)[1, 1] assert_that(is.numeric(n)) if(n < 0) { return(NULL) } if(n == 0) { print(1) } factorial <- 1 for(i in 1:n) { factorial <- i * factorial } print(factorial) }, warning = function(w) { print("Ups, a warning:") print(w) }, error = function(e) { print("There is an error:") print(e) }, finally = { print("-- That's all folks! ---") }) } > my_factorial_for_file("num.csv") [1] 120 [1] "-- That's all folks! --" > my_factorial_for_file("num2.csv") [1] "Ups, a warning:" <simpleWarning in file(file, "rt"): cannot open file 'num2.csv': No such file or directory> [1] "-- That's all folks! ---" ✓ ✓ 5 num.csv
  • 21. Comprobaciones una y otra vez sobre lo mismo > my_factorial(5) [1] 120 > my_factorial(0) [1] 1 > my_factorial(-5) [1] NULL
  • 22. Sería maravilloso que cada vez que cambiase el código se pudiera evaluar de forma sencilla Pixabay
  • 23. Test unitarios ● Solo deben fallar cuando se introduce un bug ● Los test deben acabar rápidamente, tardar menos de un minuto en ejecutarse ● Debe ser fácil detectar dónde se produce el error para poderlo solucionar Un test unitario es una secuencia de comandos que evalúa la unidad más pequeña del código y lo compara con el comportamiento esperado. Deberían: ● Los test deben ser independientes unos de otros
  • 24. Una buena nomenclatura Código que queremos testear foo.R Código de testeo tests/test.foo.R
  • 25. ¡Eh, mira, un test unitario! library("testthat") test_that("testing inputs", { expect_equal(my_factorial(5), 120) }) > test_file('tests/test.my_factorial.R') . DONE ================================= > test_file('tests/test.my_factorial.R') Error: Test failed: 'testing inputs' * my_factorial(5) not equal to 120. 1/1 mismatches [1] 0 - 120 == -120 Si my_factorial(5) devuelve 0 Si my_factorial(5) devuelve 120 Tenemos la expectativa de que my_factorial(5) devuelva 120
  • 26. ¡Eh, mira, un test unitario! library("testthat") test_that("testing inputs", { expect_equal(my_factorial(5), 120) expect_equal(my_factorial(0), 1) expect_equal(my_factorial(-1), NULL) }) > test_file('tests/test.my_factorial.R') ... DONE ==================================================================
  • 27. "testthat: Get Started with Testing" Hadley Wickham Expectation Una expectation es una afirmación binaria sobre si un valor es o no lo que se espera. Sólo en caso de no ser verdadera dará un error. Hay 11 tipos: expect_that(x, is_true()) expect_that(x, is_false()) expect_that(x, is_a(y)) expect_that(x, equals(y)) expect_that(x, is_equivalent_to(y)) expect_that(x, is_identical_to(y)) expect_that(x, matches(y)) expect_that(x, prints_text(y)) expect_that(x, shows_message(y)) expect_that(x, gives_warning(y)) expect_that(x, throws_error(y)) expect_true(x) expect_false(x) expect_is(x, y) expect_equal(x, y) expect_equivalent(x, y) expect_identical(x, y) expect_matches(x, y) expect_output(x, y) expect_message(x, y) expect_warning(x, y) expect_error(x, y) Full Short curt
  • 28. Ejemplos de expectations library("testthat") test_that("example of expectations", { expect_equal(1, 1) # pass expect_equal(1, 1 + 1e-8) # pass expect_equal(1, 5) # fail expect_identical(1, 1) # pass expect_identical(1, 1 + 1e-8) # fail expect_identical(3, 1 + 2) # pass expect_identical(0.3, 0.1 + 0.2) # fail }) > test_file('tests/test.examples.R') ..1.2.3 Failed --------------------------------------------- 1. Failure: example of expectations(@test.examples.R#6) ------------ 1 not equal to 5. 1/1 mismatches [1] 1 - 5 == -4 2. Failure: example of expectations (@test.examples.R#8) ------------------------ 1 not identical to 1 + 1e-08. Objects equal but not identical 3. Failure: example of expectations (@test.examples.R#10) ----------------------- 0.3 not identical to 0.1 + 0.2. Objects equal but not identical DONE ======================================== 1 2 3 4 5 6 7 8 9 10 11
  • 29. Ejemplos de expectations library("testthat") context("testing expectations") test_that("testing expect_true", { expect_true(TRUE) # pass expect_true(FALSE) # fail }) test_that("testing expect_is", { model <- lm(c(6:10) ~ c(1:5)) expect_is(model, "lm") # pass expect_is(model, "numeric") # fail }) > test_file('tests/test.examples.R') testing expectations: .1.2 Failed ------------------------------------------------------ 1. Failure: testing expect_true (@test.examples2.R#7) --------- FALSE isn't true. 2. Failure: testing expect_is (@test.examples2.R#13) --------- `model` inherits from `lm` not `numeric`. DONE ====================================================== 1 2 3 4 5 6 7 8 9 10 11 12 13 14
  • 30. asserthat::with_mock() with_mock() ejecuta el código después de sustituir temporalmente las implementaciones de las funciones del paquete. Podemos tener en nuestro código funciones que sean difícil de testear. ● Llamadas a un servicio que no está disponible ● Resultados impredecibles al llamar a una función ● Un código que tarde demasiado en ejecutarse ¿Cómo testear en estos casos que el código es el correcto?
  • 31. Un expectation con with_mock() library(assertthat) my_factorial_for_file <- function(filename) { tryCatch({ n <- read.csv(filename, header = FALSE)[1, 1] assert_that(is.numeric(n)) if(n < 0) { return(NULL) } if(n == 0) { print(1) } factorial <- 1 for(i in 1:n) { factorial <- i * factorial } print(factorial) }, warning = function(w) { print("Ups, a warning:") print(w) }, error = function(e) { print("There is an error:") print(e) }, finally = { # print("-- That's all folks! ---") }) } library("testthat") context("testing my_factorial_for_file()") test_that("testing inputs", { with_mock( read.csv = function(filename, header = FALSE) {data.frame(5)}, {expect_equal(my_factorial_for_file(5), 120)} ) }) > test_file('tests/test.my_factorial_for_file.R') testing my_factorial_for_file(): [1] 120 . DONE ======================================================================= ✓
  • 32. Varios expectations con with_mock() library(assertthat) my_factorial_for_file <- function(filename) { tryCatch({ n <- read.csv(filename, header = FALSE)[1, 1] assert_that(is.numeric(n)) factorial <- 1 if(n < 0) { return(NULL) } if(n == 0) { print(1) } for(i in 1:n) { factorial <- i * factorial } print(factorial) }, warning = function(w) { print("Ups, a warning:") print(w) }, error = function(e) { print("There is an error:") print(e) }, finally = { # print("-- That's all folks! ---") }) } library("testthat") context("testing my_factorial_for_file()") test_that("testing inputs", { with_mock( read.csv = function(filename, header = FALSE) {data.frame(5)}, {expect_equal(my_factorial_for_file(5), 120)} ) with_mock( read.csv = function(filename, header = FALSE) {data.frame(1)}, {expect_equal(my_factorial_for_file(0), 1)} ) }) > test_file('tests/test.my_factorial_for_file.R') testing my_factorial_for_file(): [1] 120 .[1] 1 . DONE ======================================================================= ✓
  • 33. Último reto Crear una función, add_one(), que sume uno al número que se le pase. En caso de que no se le pase un número debe devolver NULL. Utilizar la metodología Test-Driven Development (TDD).
  • 34. Workflow (basado en Test-driven development) Escribe el test tests/test.foo.R Escribe el código mínimo foo.R ¿Pasa el test? No
  • 35. Pixabay  ¿escribir el test antes que el código? ¿Me estás tomando el pelo?
  • 36.  Por qué escribir el test antes que el código ● Nos aseguramos de obtener el resultado deseado y de que efectivamente vamos a generar código testeable ● Sirve de guía si no sabes por dónde empezar pero sabes lo que quieres obtener ● Seamos realistas, lo más seguro es que una vez que el código realiza lo que queremos nos dará más pereza escribir el test
  • 37. Test unitarios y paquetes: Test workflow ● Crear un paquete ● Crear test para una función, add_one(), que sume una unidad al número que se le pase de argumento y en caso de no pasarle un número nos devuelva un texto ● Crear la función, add_one() ● Estudiar la cobertura de test del paquete
  • 38.
  • 39. Extractos de "Package Development with devtools Cheat Sheet"
  • 40. context("") test_that("", { expect_equal() }) Test unitarios y paquetes: Test workflow (anotaciones) Plantilla de test unitario:
  • 41. Test unitarios y paquetes: Test workflow (anotaciones) File –> New Project -> New Directory -> R Package devtools::use_testthat() crear carpeta test/testthat Revisar DESCRIPTION Crear test.add_one.R context("test add_one()") test_that("test add one", { expect_equal(add_one(5), 6) expect_equal(add_one(0.5), 1.5) expect_equal(add_one(-42), -41) }) test_that("test is not numeric", { expect_equal(add_one('a'), NULL) })
  • 42. Test unitarios y paquetes: Test workflow (anotaciones) Crear add_one.R devtools::test() # add_one.R add_one <- function(x){ if(!is.numeric(x)){ return(NULL) } return(x + 1) } library(covr) cov <- package_coverage() cov report(cov)
  • 43. Test unitarios y paquetes
  • 44.
  • 46. Devrant ¡Cuidado! El código puede pasar todos los test unitarios y no funcionar por problemas de integración
  • 47. Enlaces de interés ● https://journal.r-project.org/archive/2011-1/RJournal_2011-1_Wickham.pdf ● http://courses.had.co.nz/11-devtools/slides/7-testing.pdf ● https://es.slideshare.net/mobile/egoodwintx/unit-testing-in-r-with-testthat-hrug ● https://b-rodrigues.github.io/fput/unit-testing.html#introduction ● https://katherinemwood.github.io/post/testthat/ ● http://kbroman.org/Tools4RR/assets/lectures/09_testdebug_withnotes.pdf ● http://sd.jtimothyking.com/2006/07/11/twelve-benefits-of-writing-unit-tests-first/ ● https://www.is.uni-freiburg.de/ressourcen/algorithm-design-and-software-engineering-oeffentlicher-zugri ff/11_softwaretesting.pdf ● https://www.rstudio.com/wp-content/uploads/2015/03/devtools-cheatsheet.pdf ● http://r-pkgs.had.co.nz/tests.html ● http://www.machinegurning.com/rstats/test-driven-development/ ● http://stat545.com/packages05_foofactors-package-02.html
  • 49. Extra: traceback() > g <- function(x) x + "a" > g(0) Error in x + "a" : non-numeric argument to binary operator > f <- function(x) g(x) > f(0) Error in x + "a" : non-numeric argument to binary operator > traceback() 2: g(x) at #1 1: f(0)