0Pricing
R Academy · Lekcja

Testy jednostkowe za pomocą testthat

Pisz bloki test_that(), używaj asercji i uruchamiaj testy za pomocą devtools::test().

Testy jednostkowe za pomocą testthat to bezpłatna lekcja R Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej R Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs R Academy zawiera 4 lekcji w sumie.

Dlaczego testy jednostkowe?

Testy jednostkowe automatycznie sprawdzają, czy poszczególne funkcje działają prawidłowo. Wykrywają regresje po zmianie kodu, pełnią funkcję wykonywalnej dokumentacji i dają pewność, że można bezpiecznie refaktoryzować kod. Pakiet testthat to standardowy framework do testowania pakietów R.

Konfiguracja testthat

usethis::use_testthat() dodaje testthat do sekcji Suggests w pliku DESCRIPTION, tworzy katalog tests/testthat/ oraz skrypt uruchamiający tests/testthat.R. Należy uruchomić to polecenie raz podczas inicjalizowania nowego pakietu.

# 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

Tworzenie pliku testów

usethis::use_test('add') tworzy plik tests/testthat/test-add.R. Zgodnie z konwencją pliki testów noszą nazwy test-{function_name}.R. Każdy plik grupuje testy jednej funkcji lub funkcjonalności.

# 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)
# })

Struktura test_that()

test_that('description', { ... }) grupuje powiązane oczekiwania. Ciąg znaków z opisem powinien uzupełniać zdanie „test that ...” i być na tyle szczegółowy, aby komunikaty o błędach były użyteczne.

# 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() i expect_identical()

expect_equal(actual, expected) wykonuje test z tolerancją numeryczną dla liczb zmiennoprzecinkowych. expect_identical(actual, expected) wymaga dokładnej równości, także pod względem typu. W większości przypadków lepiej użyć 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() i expect_warning()

Należy sprawdzać, czy funkcje generują właściwe błędy i ostrzeżenia. Do dopasowania komunikatu błędu należy przekazać wzorzec wyrażenia regularnego — dzięki temu można upewnić się, że zgłaszany jest właściwy błąd, a nie dowolny błąd.

# 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() i expect_false()

expect_true(expr) i expect_false(expr) testują warunki logiczne. Należy ich używać podczas testowania predykatów lub warunków zwracających pojedynczą wartość logiczną.

# 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)
# })

Więcej funkcji oczekiwań

testthat udostępnia wiele funkcji oczekiwań przeznaczonych do różnych scenariuszy:

  • expect_length(x, n) — sprawdza długość wektora
  • expect_type(x, 'double') — sprawdza typ bazowy
  • expect_s3_class(x, 'data.frame') — sprawdza klasę S3
  • expect_null(x) — sprawdza, czy wartość to NULL
  • expect_match(string, regexp) — sprawdza wzorzec w ciągu znaków
# 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')
# })

Uruchamianie testów za pomocą devtools::test()

devtools::test() (Ctrl+Shift+T) uruchamia wszystkie pliki testów i wyświetla podsumowanie zaliczonych testów, niepowodzeń oraz ostrzeżeń. Przy każdym nieudanym teście wyświetlane jest niespełnione oczekiwanie oraz wartości rzeczywista i oczekiwana.

# 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 ]

Pokrycie testami za pomocą covr

covr::package_coverage() mierzy, jaki odsetek wierszy kodu pakietu jest wykonywany przez testy. covr::report() otwiera raport HTML pokazujący wiersze objęte testami (na zielono) i nieobjęte testami (na czerwono). Należy dążyć do pokrycia wynoszącego co najmniej 80%.

# 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

Testowanie przypadków brzegowych

Dobre testy obejmują nie tylko typowy przebieg, lecz także przypadki brzegowe:

  • Puste dane wejściowe: numeric(0), character(0)
  • Dane wejściowe NA: czy funkcja przekazuje dalej wartości NA, czy je obsługuje?
  • Dane wejściowe o długości 1 i o długości n
  • Wartości graniczne: 0, liczby ujemne, bardzo duże wartości
  • Nieprawidłowe typy: co się stanie, gdy użytkownik przekaże ciąg znaków do funkcji numerycznej?
# 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)
# })

Szybkie sprawdzenie: expect_error()

Wywołują Państwo expect_error(my_fn('bad'), regexp = 'invalid input'). Co sprawdza ten test?

Powtórzenie: testy jednostkowe

Testowanie pakietu R za pomocą testthat:

  • usethis::use_testthat() — jednorazowe skonfigurowanie infrastruktury testowej
  • usethis::use_test('fn') — utworzenie pliku tests/testthat/test-fn.R
  • test_that('description', {...}) — grupowanie powiązanych oczekiwań
  • expect_equal(), expect_error(), expect_warning(), expect_true(), expect_false() — podstawowe funkcje oczekiwań
  • devtools::test() — uruchomienie wszystkich testów (Ctrl+Shift+T)
  • covr::package_coverage() — pomiar pokrycia testami

Często zadawane pytania

Czy lekcja „Testy jednostkowe za pomocą testthat” jest bezpłatna?

Tak — pełny tekst „Testy jednostkowe za pomocą testthat” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu R Academy, przejdź na CoddyKit PRO. Kurs R Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Testy jednostkowe za pomocą testthat”?

Pisz bloki test_that(), używaj asercji i uruchamiaj testy za pomocą devtools::test(). Ćwiczysz R Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć R Academy?

Nie wymagamy żadnego doświadczenia. R Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Testy jednostkowe za pomocą testthat”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji R Academy?

Tak. Każda lekcja R Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Struktura pakietu za pomocą usethis i devtools
  2. Dokumentowanie funkcji za pomocą roxygen2
  3. Testy jednostkowe za pomocą testthat
  4. Przesyłanie pakietu do CRAN i jego utrzymanie
← Powrót do R Academy