0Pricing
R Academy · Lezione

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: 3

Creazione 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 vettore
  • expect_type(x, 'double') — controlla il tipo di base
  • expect_s3_class(x, 'data.frame') — controlla la classe S3
  • expect_null(x) — controlla che il valore sia NULL
  • expect_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 lines

Test 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 test
  • usethis::use_test('fn') — crea tests/testthat/test-fn.R
  • test_that('description', {...}) — raggruppa le aspettative correlate
  • expect_equal(), expect_error(), expect_warning(), expect_true(), expect_false() — funzioni principali per le aspettative
  • devtools::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

  1. Struttura dei pacchetti con usethis e devtools
  2. Documentazione delle funzioni con roxygen2
  3. Test unitari con testthat
  4. Invio a CRAN e manutenzione dei pacchetti
← Torna a R Academy