Testing von R Paketen

Markus Berroth Blog

Der letzte Blogbeitrag zeigte warum Testing sinnvoll ist und wie sich der Workflow für einzelne Skripte mit dem testthat-Package gestalltet. Dieser Beitrag soll sich um die Integration von Unit-Testing für eigene Pakete drehen.

Set-Up

Der einfachste Weg, um Unit-Testing mit dem testthat-Package einzurichten ist devtools::use_testthat() laufen zu lassen. Dies bewirkt mehrere Dinge:

  1. Es erzeugt das Verzeichnis tests/testthat.
  2. Es fügt testthat in das Suggests-Feld in DESCRIPTION hinzu. Dies bedeutet das testthat nicht notwendigerweise benötigt wird um das Package laufen zu lassen, es aber benutzt wird und Vorteile daraus zieht.
  3. Außerdem wird die Datei tests/testthat.R erzeugt. Dadurch werden alle Tests während dem R CMD check laufen gelassen.

Beispiel bounceR

Den weitere Ablauf werde ich an Hand des ersten STATWORX R-Packages bounceR aufzeigen. bounceR ist ein Package zur automatisierten Feature Selection und wird bald ausführlicher von meinem Kollegen Lukas hier vorgestellt.

Alle Test-Files werden in dem neu erschaffenen Ordner tests/testthat abgelegt. Um zu schauen ob der Input korrekt überprüft wird, lege ich folgendes neues File test_input.R innerhalb von tests/testthat an.

context("Input is checked")

test_that("target is not missing",{
  train_df <- sim_data()
  expect_error(featureSelection(data = train_df, ),
               "I cannot find your target")
  expect_error(featureSelection(data = train_df, target = "target"),
               "The data does not contain the target!")
})

test_that("target is specified correct",{
  train_df <- sim_data()
  expect_error(featureSelection(data = train_df, target = train_df$y ),
               "Just give me the name of the target column")
  expect_error(featureSelection(data = train_df, target = 1 ),
               "Just give me the name of the target column")
  expect_error(featureSelection(data = train_df, target = as.data.frame("y")),
               "Just give me the name of the target column")        
})

Wie man sieht bleibt der restliche Aufbau für das Testing mit context, test_that und expectations derselbe wie im vorherigen Blog-Post beschrieben.

Um sämtliche geschriebene Tests für das Package auszuführen, kann enweder Ctrl/Cmd + Shift + T innerhalb von R Studio gedrückt oder devtools::test() ausgeführt werden. Wodurch folgender Output erzeugt wird:

devtools::test output

Es wird wieder jedes Test-File einzeln mit dem jeweiligen Context aufgelistet. Ein grüner Punkt bedeutet, dass der Test bestanden wurde und eine rote Zahl, dass dies nicht so ist und mit der Angabe um welchen Test es sich dabei handelt.

Hinweise für CRAN

Zu Beginn wurde ein neues File unter tests/testthat.R erzeugt. Dies bedeutet, dass alle Test auf CRAN für die Plattformen Windows, Mac, Linux und Solaris laufen gelassen werden. Hierbei gibt es ein paar Dinge zu beachten:

  1. Tests sollten relativ schnell laufen, eine Richtlinie hierfür ist etwa unter einer Minute. Daher sollte man skip_on_cran() zu Beginn eines langen Testes benutzten. Dadurch wird der Test nur lokal ausgeführt, jedoch nicht auf CRAN.
  2. Alle Tests werden in englischer Sprache (LANGUAGE=EN) und mit der Option C sort order (LC_COLLATE=C) ausgeführt. Dadurch können Test fehlschlagen, falls lokal andere Optionen benutzt werden.
  3. Es sollten keine Code-Teile getestet werden, welche sich zwischen Computer unterscheiden können und dadurch auf dem CRAN-Server fehschlagen können. Daher sollte man beispielsweise vorsichtig sein, die Laufzeit oder Multi-Core Unterstützung zu testen. Ebenfalls kann sich die numerische Präzesion unterscheiden, wodurch lieber expect_equal() anstelle von expect_identical() benutzt werden sollte.

Nachdem das bounceR-Paket vorgestellt wurde, werde ich mögliche Testing-Szenarien und Strategien für dieses Paket aufzeigen.

Über den Autor
Markus Berroth

Markus Berroth

Markus ist Mitglied in unserem Data Science Team und Python Experte. In seiner Freizeit geht er gerne übers Wochenende auf Städtetrips.