Test unitari con testthat
Scriva blocchi test_that(), utilizzi le expectations ed esegua i test con devtools::test()
Test unitari con testthat è una lezione R Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento R Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso R Academy include 4 lezioni in totale.
Perché eseguire test unitari?
I test unitari verificano automaticamente che le singole funzioni si comportino correttamente. Rilevano le regressioni quando modifica il codice, fungono da documentazione eseguibile e consentono di effettuare il refactoring in sicurezza. Il pacchetto testthat è il framework di test standard per i pacchetti R.
Configurazione di testthat
usethis::use_testthat() aggiunge testthat a Suggests in DESCRIPTION, crea tests/testthat/ e crea lo script runner tests/testthat.R. Lo esegua una volta durante l'inizializzazione di un nuovo pacchetto.
# 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: 3Creazione di un file di test
usethis::use_test('add') crea tests/testthat/test-add.R. Per convenzione, i file di test vengono denominati test-{function_name}.R. Ogni file raggruppa i test di una funzione o di una funzionalità.
# 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)
# })Struttura di test_that()
test_that('description', { ... }) raggruppa aspettative correlate. La stringa descrittiva dovrebbe completare la frase «test che ...» ed essere abbastanza specifica da risultare utile nei messaggi di errore.
# 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() ed expect_identical()
expect_equal(actual, expected) esegue il test con una tolleranza numerica per i numeri in virgola mobile. expect_identical(actual, expected) richiede un'uguaglianza esatta, incluso il tipo. Nella maggior parte dei casi è preferibile 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() ed expect_warning()
Verifichi che le funzioni producano gli errori e gli avvisi corretti. Passi un pattern regex per confrontarlo con il messaggio di errore: in questo modo verifica che venga generato l'errore corretto, non un errore qualsiasi.
# 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() ed expect_false()
expect_true(expr) ed expect_false(expr) verificano condizioni logiche. Li usi per testare predicati o condizioni che restituiscono un singolo valore logico.
# 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)
# })Altre funzioni di aspettativa
testthat fornisce numerose funzioni di aspettativa per diversi scenari:
expect_length(x, n)— controlla la lunghezza del vettoreexpect_type(x, 'double')— controlla il tipo di baseexpect_s3_class(x, 'data.frame')— controlla la classe S3expect_null(x)— controlla che il valore sia NULLexpect_match(string, regexp)— controlla il pattern della stringa
# 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')
# })Esecuzione dei test con devtools::test()
devtools::test() (Ctrl+Shift+T) esegue tutti i file di test e mostra un riepilogo dei test superati, falliti e degli avvisi. I singoli test falliti mostrano l'aspettativa che non è stata soddisfatta e i valori effettivi rispetto a quelli attesi.
# 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 ]Copertura dei test con covr
covr::package_coverage() misura la percentuale di righe del pacchetto eseguite dai test. covr::report() apre un report HTML che mostra le righe coperte (in verde) e non coperte (in rosso). Punti ad almeno l'80% di copertura.
# 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 linesTest dei casi limite
Test efficaci coprono non solo il percorso previsto, ma anche i casi limite:
- Input vuoti:
numeric(0),character(0) - Input NA: la funzione propaga o gestisce NA?
- Input di lunghezza 1 rispetto a input di lunghezza n
- Valori limite: 0, numeri negativi, valori molto grandi
- Tipi errati: che cosa accade quando l'utente passa una stringa a una funzione numerica?
# 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)
# })Verifica rapida: expect_error()
Si chiama expect_error(my_fn('bad'), regexp = 'invalid input'). Che cosa verifica questo test?
Riepilogo dei test unitari
Testare un pacchetto R con testthat:
usethis::use_testthat()— configura una sola volta l'infrastruttura dei testusethis::use_test('fn')— creatests/testthat/test-fn.Rtest_that('description', {...})— raggruppa le aspettative correlateexpect_equal(),expect_error(),expect_warning(),expect_true(),expect_false()— funzioni principali per le aspettativedevtools::test()— esegue tutti i test (Ctrl+Shift+T)covr::package_coverage()— misura la copertura dei test
Domande Frequenti
La lezione «Test unitari con testthat» è gratuita?
Sì — il testo completo di «Test unitari con testthat» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso R Academy, passa a CoddyKit PRO. Il corso R Academy include 4 lezioni in totale.
Cosa imparerò in «Test unitari con testthat»?
Scriva blocchi test_that(), utilizzi le expectations ed esegua i test con devtools::test() Eserciti R Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare R Academy?
Non è richiesta alcuna esperienza precedente. R Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.
Quanto tempo richiede la lezione «Test unitari con testthat»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione R Academy?
Sì. Ogni lezione R Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Struttura dei pacchetti con usethis e devtools
- Documentazione delle funzioni con roxygen2
- Test unitari con testthat
- Invio a CRAN e manutenzione dei pacchetti