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

dittodb

simplificando las pruebas con bases de datos

Mauricio ‘Pachá’ Vargas · PUC Chile

ConectaR 2021

2021-02-04

¿En qué consisten las pruebas?

Una forma de verificar que el código que escribimos haga lo que esperamos que haga.

Es más fácil decirlo que hacerlo



Cualquier prueba es mejor que no hacer pruebas

Es más fácil decirlo que hacerlo



Cualquier prueba es mejor que no hacer pruebas

pero una buena prueba vale su peso en oro (metafóricamente)

Una buena prueba es

  • de rápida ejecución
  • expresiva
  • no requiere configuraciones especiales

Una buena prueba es

  • de rápida ejecución
  • expresiva
  • no requiere configuraciones especiales

Pero las bases de datos son

  • (relativamente) lentas
  • dependen de variables o configuraciones externas
  • requieren una conexión o configuración personalizada

IC + BBDD = ☠️

La Integración Continua (como GitHub Actions, Travis, AppVeyor) es maravillosa, permite ejecutar código en la ☁️ 💻 de alguien más.

IC + BBDD = ☠️

La Integración Continua (como GitHub Actions, Travis, AppVeyor) es maravillosa, permite ejecutar código en la ☁️ 💻 de alguien más.

Pero para correr el código en un entorno debemos configurarlo.

IC + BBDD = ☠️

La Integración Continua (como GitHub Actions, Travis, AppVeyor) es maravillosa, permite ejecutar código en la ☁️ 💻 de alguien más.

Pero para correr el código en un entorno debemos configurarlo.No se puede iterar de forma interactiva al hacer esto.

IC + BBDD = ☠️

La Integración Continua (como GitHub Actions, Travis, AppVeyor) es maravillosa, permite ejecutar código en la ☁️ 💻 de alguien más.

Pero para correr el código en un entorno debemos configurarlo.No se puede iterar de forma interactiva al hacer esto.

Incluso instalar dependencias de R puede ser un problema.

Ya pasamos por esto
para que no tengas que hacerlo

¿Como ayuda dittodb?

Imagina que tenemos una función

#' Get random airline(s)
#' ...
get_an_airline <- function(n = 1) {
con <- DBI::dbConnect(RPostgres::Postgres(), dbname = "nycflights")
on.exit(DBI::dbDisconnect(con))
query <- paste0(
"SELECT carrier, name FROM airlines ",
"ORDER BY random() LIMIT ",
n
)
out <- dbGetQuery(con, query)
if (out$name == "Testy McAirline") {
stop("This is the test airline 🙃")
}
return(out)
}

¿Cómo podemos probar la función?

Localmente es "fácil":

  • configuramos un servidor PostgreSQL
  • nos aseguramos de que exista el usuario
  • agregamos los datos de prueba
  • obtenemos el beneficio

¿Cómo podemos probar la función?

Localmente es "fácil":

  • configuramos un servidor PostgreSQL
  • nos aseguramos de que exista el usuario
  • agregamos los datos de prueba
  • obtenemos el beneficio

Con una IC hacemos "exactamente" lo mismo:

  • configuramos un servidor PostgreSQL
  • nos aseguramos de que exista el usuario
  • agregamos los datos de prueba

¿Cómo podemos probar la función?

Localmente es "fácil":

  • configuramos un servidor PostgreSQL
  • nos aseguramos de que exista el usuario
  • agregamos los datos de prueba
  • obtenemos el beneficio

Con una IC hacemos "exactamente" lo mismo:

  • configuramos un servidor PostgreSQL
  • nos aseguramos de que exista el usuario
  • agregamos los datos de prueba
    ️ ☠️ ☠️ ☠️

dittodb te permite

Escribir pruebas que operan de igual modo que si estuvieramos conectados a una base de datos real usando with_mock_db({...}) y sin la necesidad de configurar una base de datos.


with_mock_db({
test_that("We get one airline", {
one_airline <- get_an_airline()
expect_is(one_airline, "data.frame")
expect_equal(nrow(one_airline), 1)
expect_equal(one_airline$carrier, "9E")
expect_equal(one_airline$name, "Endeavor Air Inc.")
})
})

Terminología de pruebas

  • mocks -- objetos pre-programados con un valor esperado y que constituyen una especificación
  • fixtures -- datos usados por los objetos de tipo mock para configurarse o proveer información
  • stubs -- proveen respuestas embebidas a llamadas durante las pruebas, habitualmente no responden a cualquier cosa fuera del propósito para el cual se escribieron en la prueba
  • spies -- stubs, pero proporcionan información acerca de cómo se llamaron
  • fakes -- objetos que tienen una implementación, pero habitualmente toma un atajo que no los hace convenientes para su uso en producción (una BBDD en memoria es un buen ejemplo)
  • 😱

Referencia: https://www.martinfowler.com/articles/mocksArentStubs.html

Capturando interacciones

Captura las interacciones con BBDD para que escribir y editar lo siguiente sea fácil.

capture_db_requests({
one_airline <- get_an_airline()
})


Donde one_airline es

carrier name
1 AS Alaska Airlines Inc.

Capturando interacciones

La respuesta se guarda en testthat/tests/nycflights en el archivo SELECT-c3427d.R:

structure(
list(carrier = "AS", name = "Alaska Airlines Inc."),
class = "data.frame",
row.names = c(NA, -1L)
)

Capturando interacciones

La respuesta se guarda en testthat/tests/nycflights en el archivo SELECT-c3427d.R:

structure(
list(carrier = "AS", name = "Alaska Airlines Inc."),
class = "data.frame",
row.names = c(NA, -1L)
)

La ruta es testthat/tests y luego el nombre de la BBDD nycflights a la que nos conectamos.

Capturando interacciones

La respuesta se guarda en testthat/tests/nycflights en el archivo SELECT-c3427d.R:

structure(
list(carrier = "AS", name = "Alaska Airlines Inc."),
class = "data.frame",
row.names = c(NA, -1L)
)

La ruta es testthat/tests y luego el nombre de la BBDD nycflights a la que nos conectamos.

El nombre de archivo comienza con el verbo SQL SELECT, seguido de un identificador único para la query.

Ya que el identificador es único, el resultado siempre será el mismo. La forma de modificar esto es cambiar la ruta.

dittodb permite

Escribir tests como el siguiente

with_mock_db({
test_that("We get one airline", {
one_airline <- get_an_airline()
expect_is(one_airline, "data.frame")
expect_equal(nrow(one_airline), 1)
expect_equal(one_airline$carrier, "9E")
expect_equal(one_airline$name, "Endeavor Air Inc.")
})
})

... ¡se puede ejecutar en cualquier parte!

dittodb permite

Escribir casos que incluyen condiciones de borde (e.g. cosas que no deberían suceder en la BBDD)

with_mock_path(path = "error_db_fixt/", {
with_mock_db({
test_that("We get one airline", {
expect_error(
get_an_airline(),
"This is the test airline \U1F643
)
})
})
})

¡Es simple!

Demo

¡Gracias!

dittodb es parte de rOpenSci y está documentado en dittodb.jonkeane.com

Se puede instalar desde CRAN mediante install.packages("dittodb")

Agradecimientos especiales a Jonathan Keane @jonkeane y a httptest por la inspiración.

¿En qué consisten las pruebas?

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