FoRt: Paketerstellung mit devtools & testthat Fabian Scheipl Institut für Statistik LMU München Entwicklungsstil Übliches Vorgehen Entwicklung eines einzelnen Skripts: • herumprobieren in der Kommandozeile • modifiziere bis Rechner gehorcht, dann kopiere Befehle in ein File ODER • Arbeit mit mehreren Files • source() auf alles & Output begutachten • modifizieren & wiederholen Beides ist chaotisch & fehlerträchtig wenn man saubere, reproduzierbare Analysen oder eben Pakete entwickeln möchte. Entwicklungsstile • konfirmatorisches Programmieren: nur das “wie” ist unklar – Programmiererin weiß genau was sie tun muss & welches Ergebnis die vorzunehmenden Änderungen haben sollen – test-driven development • exploratives Programmieren: – lose Idee was am Ende rauskommen soll – nicht sicher über Dinge wie die richtige Kapselung der Sub-Funktionen, Details der inputs/outputs, etc. • möglichst konfirmatorisch arbeiten wenn man etwas neues von Grund auf entwickelt (top-down programming!) • Debugging und Feature-Erweiterung zu Beginn oft eher explorativ devtools Paketentwicklung ohne devtools Kommandozeilen-Befehle für ein Quellcode-Paket im Ordner pkg: • R CMD SHLIB pkg kompiliert C/C++/Fortran-Code in pkg/src • R CMD check pkg überprüft ob pkg ein valides R-Paket ist 1 • R CMD build pkg erzeugt package bundle pkg.tar.gz bzw. pkg.zip • mit devtools direkt aus R heraus aufrufbar. • mit devtools & RStudio per Button / Tastenkombi. Unter Windows: benötigt zusätzlich Installation von Rtools. Wichtige devtools Befehle • • • • dev_mode(): schalte in developer mode (spezielle .libPaths()) load_all("pkg"): simuliert library("pkg") test("pkg"): lässt Tests in inst/tests/ laufen und gibt Ergebnisse aus document("pkg"): lässt roxygen2::roxygenize über das Paket laufen um Hifeseiten, NAMESPACE, etc. zu aktualisieren Wichtige devtools Befehle Sobald alles zu funktionieren scheint: • check_doc("pkg") um erzeugte Hilfeseiten zu validieren • run_examples("pkg") um Beispiele zu validieren • check("pkg") um vollständiges R CMD check durchzuführen, mit den selben Optionen wie auf CRAN (aber nur auf einer R-Version!) • build("pkg") und build_win("pkg") um bundles und Windows binaries zu erstellen. Paket-Erstellung in RStudio • Neues Projekt als “Neues R-Paket” anlegen • Stellt alle diese Funktionen als Buttons/Shortcuts zur Verfügung Exploratives Programmieren Workflow: 1. 2. 3. 4. 5. 6. ändere Code und lade neu mit load_all() begutachte Effekt der Änderungen wiederhole 1. und 2. bis Ziel erreicht (aktualisiere Doku, generiere Hilfe mit document()) check("pkg") build("pkg") In RStudio einfach über Buttons/Shortcuts 2 Konfirmatorisches Programmieren Workflow: 1. schreibe Test für Bug oder zu implementierendes Feature. Stelle sicher das der Test versagt. 2. ändere Code und lade neu mit load_all() 3. lasse alle Tests die sich für das Paket angesammelt haben laufen (mit test() falls testthat benutzt wird, sonst per source() der Testscripts.) 4. wiederhole 2. und 3. bis kein Test mehr versagt 5. (aktualisiere Doku, generiere Hilfe mit document()) 6. check("pkg") 7. build("pkg") In RStudio einfach über Buttons/Shortcuts. Konfirmatorisches Programmieren • weniger Frust, weniger Bugs • bessere Codestruktur: – leicht zu testen ⇔ gutes Design (Kapselung!) – defensives Programmieren: Tests schreiben heißt Fehlerquellen antizipieren • leichter Wiedereinstieg nach Pause – versagende Tests als to do Liste • mehr Selbstvertrauen um große Änderungen zu wagen: – Verschlimmbesserungen sofort sichtbar falls Testabdeckung gut genug ist testthat testthat für konfirmatorisches Programmieren hierarchische Struktur: • expectation() beschreibt das erwartete Ergebnis eines Befehls – Wert, Klasse, korrekte Fehlermeldung, Rechenzeit, . . . ? – definiert mit expect_that() • test_that() gruppiert mehrere expectations für eine Funktion oder ein Feature • context() gruppiert mehrere thematisch verwandte Tests expectation • überprüft ob ein Wert einer Erwartung entspricht. Wenn nicht, dann error • Syntax z.B. expect_that(a, equals(b)) 3 Expectations Vollständig Abkrzng expect_that(x, is_true()) expect_true(x) expect_that(x, is_false()) expect_false(x) expect_that(x, is_a(y)) expect_is(x, y) expect_that(x, equals(y)) expect_equal(x, y) expect_that(x, is_equivalent_to(y)) expect_equivalent(x, y) expect_that(x, is_identical_to(y)) expect_identical(x, y) expect_that(x, throws_error(y)) expect_error(x, y) expect_that(x, gives_warning(y)) expect_warning(x, y) expect_that(x, matches(y)) expect_match(x, y) expect_that(x, prints_text(y)) expect_output(x, y) expect_that(x, shows_message(y)) expect_message(x, y) expect_that(x, takes_less_than(time)) Tests • Fokus auf eine einzige, wohl-definierte Teilfunktionalität • informative Namen vergeben die den Satz “Test that . . . ” vervollständigen. • jeder Test läuft in seiner eigenen Umgebung, muss also in sich abgeschlossen sein library(testthat) test_that("trigonometric functions match identities.", { expect_that(sin(pi / 4), equals(1 / sqrt(2))) expect_that(cos(pi / 4), equals(1 / sqrt(2))) expect_that(tan(pi / 4), equals(1)) }) 4 Tests die versagen produzieren aussagekräftige Fehler: test_that("this will end badly.", { expect_equal(cos(0), 0) }) Error: Test failed: 'this will end badly.' Not expected: cos(0) not equal to 0 Mean absolute difference: 1. context . . . gruppiert mehrere thematisch verwandte Tests. library(testthat) context("Accuracy for trig functions:") test_that("trigonometric functions match identities", { expect_that(sin(pi / 4), equals(1 / sqrt(2))) expect_that(cos(pi / 4), equals(1 / sqrt(2))) expect_that(tan(pi / 4), equals(1)) }) test_that("trigonometric functions remain accurate for large arguments", { expect_that(sin(2e7 * pi + pi / 4), equals(1 / sqrt(2))) expect_that(cos(2e7 * pi + pi / 4), equals(1 / sqrt(2))) expect_that(tan(2e7 * pi + pi / 4), equals(1)) }) Tests testen Ausserhalb eines Pakets: • testthat::test_file() oder testthat::test_dir() um alle Tests in einer Datei / einem Ordner laufen zu lassen. Namen von Test-Skripten fangen mit “test-” an. Innerhalb eines Pakets: • • • • testthat::test_package(<pkgname>), testthat::test_check() devtools::test() R CMD check, devtools::check() R Studio Button / Shortcut Ctrl-Shift-T R CMD check • alle Testfiles in Unterordner tests/testthat • Namen von Test-Files fangen mit “test-” an. • Datei tests/test-all.R mit 5 library(testthat) test_check("pkgname") • anlegen mit devtools::use_testthat() • DESCRIPTION muss Suggests: testthat enthalten • R CMD check versagt wenn nicht alle Tests erfolgreich verlaufen 6
© Copyright 2024 ExpyDoc