Pruebas unitarias con testthat
Escriba bloques test_that(), use expectations y ejecute pruebas con devtools::test().
Pruebas unitarias con testthat es una lección gratuita de R Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de R Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de R Academy incluye 4 lecciones en total.
¿Por qué realizar pruebas unitarias?
Las pruebas unitarias verifican automáticamente que las funciones individuales se comporten correctamente. Detectan regresiones al modificar el código, sirven como documentación ejecutable y le dan confianza para refactorizar de forma segura. El paquete testthat es el framework de pruebas estándar para los paquetes de R.
Configurar testthat
usethis::use_testthat() añade testthat a Suggests en DESCRIPTION, crea tests/testthat/ y crea el script ejecutor tests/testthat.R. Ejecútelo una vez al inicializar un paquete nuevo.
# usethis::use_testthat()
#
# Creates:
# tests/
# testthat.R <- runner (do not edit)
# testthat/
# (empty — write test files here)
#
# Updates DESCRIPTION:
# Suggests: testthat (>= 3.0.0)
# Config/testthat/edition: 3Crear un archivo de pruebas
usethis::use_test('add') crea tests/testthat/test-add.R. Por convención, los archivos de pruebas se nombran test-{function_name}.R. Cada archivo agrupa las pruebas de una función o característica.
# usethis::use_test('add') # creates tests/testthat/test-add.R
#
# Content of test-add.R:
# test_that('add() returns correct sum', {
# expect_equal(add(1, 2), 3)
# expect_equal(add(-1, 1), 0)
# expect_equal(add(0.1, 0.2), 0.3, tolerance = 1e-7)
# })Estructura de test_that()
test_that('description', { ... }) agrupa expectativas relacionadas. La cadena de descripción debe completar la oración «test that ...» y ser lo bastante específica para resultar útil en los mensajes de error.
# Good test_that descriptions:
# test_that('add() handles negative numbers', { ... })
# test_that('add() recycles length-1 vectors', { ... })
# test_that('add() returns NA when input contains NA', { ... })
#
# Bad (too vague):
# test_that('it works', { ... })
# test_that('test1', { ... })expect_equal() y expect_identical()
expect_equal(actual, expected) realiza la comprobación con una tolerancia numérica para los números de coma flotante. expect_identical(actual, expected) requiere igualdad exacta, incluido el tipo. En la mayoría de los casos, es preferible expect_equal().
# test_that('add() adds correctly', {
# expect_equal(add(1, 2), 3) # numeric equality
# expect_equal(add(0.1, 0.2), 0.3) # tolerance handles floating point
# expect_identical(add(1L, 2L), 3L) # exact type match: integer
# expect_identical(add(1.0, 2.0), 3.0) # exact type match: double
# })expect_error() y expect_warning()
Compruebe que las funciones produzcan los errores y advertencias correctos. Pase un patrón de expresión regular para coincidir con el mensaje de error; así se asegura de que se produzca el error adecuado y no simplemente cualquier error.
# test_that('add() validates input types', {
# expect_error(
# add('a', 2),
# regexp = 'numeric' # message must contain 'numeric'
# )
# expect_error(
# add(NULL, 1),
# regexp = 'numeric'
# )
# })
#
# test_that('sqrt() warns on negative input', {
# expect_warning(sqrt(-1))
# })expect_true() y expect_false()
expect_true(expr) y expect_false(expr) comprueban condiciones lógicas. Utilícelas al probar predicados o condiciones que devuelvan un único valor lógico.
# test_that('is_positive() returns correct logical', {
# expect_true(is_positive(5))
# expect_true(is_positive(0.001))
# expect_false(is_positive(0))
# expect_false(is_positive(-3))
# })
#
# # Also useful for vector tests:
# test_that('add() result has correct length', {
# result <- add(c(1,2,3), c(4,5,6))
# expect_true(length(result) == 3)
# })Más funciones de expectativa
testthat proporciona numerosas funciones de expectativa para distintos casos:
expect_length(x, n)— comprobar la longitud de un vectorexpect_type(x, 'double')— comprobar el tipo baseexpect_s3_class(x, 'data.frame')— comprobar la clase S3expect_null(x)— comprobar si es NULLexpect_match(string, regexp)— comprobar el patrón de una cadena
# test_that('add() output has correct type and length', {
# result <- add(c(1.0, 2.0), c(3.0, 4.0))
# expect_type(result, 'double')
# expect_length(result, 2)
# })
#
# test_that('summary_stats() returns a data frame', {
# result <- summary_stats(rnorm(100))
# expect_s3_class(result, 'data.frame')
# })Ejecutar pruebas con devtools::test()
devtools::test() (Ctrl+Shift+T) ejecuta todos los archivos de pruebas y muestra un resumen de pruebas superadas, fallos y advertencias. Los fallos individuales muestran la expectativa que no se cumplió y los valores real y esperado.
# devtools::test()
#
# Example output:
# == Testing mypackage ====================================
# v | OK F W S | Context
# v | 3 | add [0.1s]
# v | 4 | subtract [0.1s]
# x | 2 1 | multiply [0.2s]
# -- Failure (test-multiply.R:5): multiply() handles zero
# multiply(5, 0) not equal to 0.
# Actual: 5
# Expected: 0
# ==========================================================
# [ FAIL 1 | WARN 0 | SKIP 0 | PASS 9 ]Cobertura de pruebas con covr
covr::package_coverage() mide qué porcentaje de las líneas del paquete ejecutan las pruebas. covr::report() abre un informe HTML que muestra las líneas cubiertas (en verde) y no cubiertas (en rojo). Procure alcanzar al menos un 80 % de cobertura.
# library(covr)
# cov <- package_coverage()
# print(cov)
#
# Example output:
# mypackage Coverage: 87.50%
# R/add.R: 100.00%
# R/subtract.R: 100.00%
# R/utils.R: 62.50% <- needs more tests!
#
# covr::report() # interactive HTML report
# covr::zero_coverage(cov) # list uncovered linesProbar casos límite
Las buenas pruebas cubren no solo el caso habitual, sino también los casos límite:
- Entradas vacías:
numeric(0),character(0) - Entradas NA: ¿la función propaga o gestiona NA?
- Entradas de longitud 1 frente a entradas de longitud n
- Valores límite: 0, números negativos y valores muy grandes
- Tipos incorrectos: ¿qué ocurre cuando el usuario pasa una cadena a una función numérica?
# test_that('add() handles edge cases', {
# expect_equal(add(numeric(0), numeric(0)), numeric(0)) # empty
# expect_true(is.na(add(NA, 1))) # NA propagation
# expect_equal(add(1, c(1,2,3)), c(2,3,4)) # recycling
# expect_equal(add(.Machine$integer.max, 0L), # boundary
# .Machine$integer.max)
# })Comprobación rápida: expect_error()
Ejecuta expect_error(my_fn('bad'), regexp = 'invalid input'). ¿Qué comprueba esta prueba?
Repaso de pruebas unitarias
Pruebas de un paquete de R con testthat:
usethis::use_testthat()— configura la infraestructura de pruebas una sola vezusethis::use_test('fn')— creatests/testthat/test-fn.Rtest_that('description', {...})— agrupa expectativas relacionadasexpect_equal(),expect_error(),expect_warning(),expect_true(),expect_false()— funciones principales de expectativasdevtools::test()— ejecuta todas las pruebas (Ctrl+Shift+T)covr::package_coverage()— mide la cobertura de pruebas
Preguntas frecuentes
¿La lección «Pruebas unitarias con testthat» es gratis?
Sí — el texto completo de «Pruebas unitarias con testthat» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de R Academy, actualiza a CoddyKit PRO. El curso de R Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Pruebas unitarias con testthat»?
Escriba bloques test_that(), use expectations y ejecute pruebas con devtools::test(). Practicas R Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar R Academy?
No se requiere experiencia previa. R Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «Pruebas unitarias con testthat»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de R Academy?
Sí. Cada lección de R Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Estructura de paquetes con usethis y devtools
- Documentación de funciones con roxygen2
- Pruebas unitarias con testthat
- Envío a CRAN y mantenimiento de paquetes