I'm writing some tests for an R package and would like to have R CMD check
verify that functions display the correct warnings for certain inputs. But I can't figure out how to capture the warning output so that I can test it.
So if I have a function like:
throwsWarning<-function(x){
if(x>0){
warning('Argument "x" is greater than zero, results may be incorrect')
}
# do something useful ...
}
I'd want a something in my test file like:
warningOutput <-try( throwsWarning(1))
if (warningOutput!='Argument "x" is greater than zero, results may be incorrect'){
stop('function "throwsWarning" did not produce correct warning when x>0')
}
So far I've found possible partial solutions by changing options
so that warnings are treated as errors and the surrounding with a trycatch
block. Also considered testing value of last.warning
, but that seems dangerous if the warning is not thrown (would test previous value). Seems like there must be an easy way to do this that I'm missing?
Assuming you're in a package directory, just run usethis::use_test("name") to create a test file, and set up all the other infrastructure you need. If you're using RStudio, press Cmd/Ctrl + Shift + T (or run devtools::test() if not) to run all the tests in a package.
You can use traceback() to locate where the last error occurred. Usually it will point you to a call you make in your function. Then I typically put browser() at that point, run the function again and see what is going wrong. The number means how deep we are in the nested functions.
8.3 Ignoring conditions The simplest way of handling conditions in R is to simply ignore them: Ignore errors with try() . Ignore warnings with suppressWarnings() . Ignore messages with suppressMessages() .
If you’re using RStudio, press Cmd/Ctrl + Shift + T (or run devtools::test () if not) to run all the tests in a package.
It usually measures coverage as a ratio, e.g. 60% of all lines, functions, etc. If you want to check the coverage of a package, you simply have to: Say you have this R script (and its companion test): In the “Source” tab you will see a report that shows which lines have been tested and which have not.
Assuming you’re in a package directory, just run usethis::use_test ("name") to create a test file, and set up all the other infrastructure you need. If you’re using RStudio, press Cmd/Ctrl + Shift + T (or run devtools::test () if not) to run all the tests in a package.
testthat is the most popular unit testing package for R and is used by thousands of CRAN packages. If you’re not familiar with testthat, the testing chapter in R packages gives a good overview, along with workflow advice and concrete examples. The easiest way to get started is with usethis.
The testthat package has an expect_warning
and gives_warning
function that you can use.
From the examples, you would do something like this:
R> library(testthat)
R> expect_that(warning("this is a warning"), gives_warning("is a"))
## This does not raise an error, but:
R> expect_that(warning("this is a warning"), gives_warning("nope"))
Error: warning("this is a warning") does not match 'nope'. Actual value:
this is a warning
So, gives_warning
is regular expression that is matched against the warning that is supposed to be emitted. If the regex does not match (or no warning is thrown), then a red flag is raised.
Equally, using the shorter expect_warning
:
R> expect_warning(warning("this is a warning"), "is a")
If you're writing your own package, it might make sense to make use of R's condition system by throwing (and catching) particular types of errors or warnings. So
myFun <- function(x) {
if (any(is.na(x))) {
w <- simpleWarning("'x' contains NA values")
class(w) <- c("HasNA", class(w))
warning(w)
}
if (any(x < 0))
warning("'x' contains values less than 0")
x
}
and then in your test, e.g., with library(RUnit)
, use tryCatch
and pick off just the conditions that you're interested in testing, i.e., warnings with class HasNA
:
test_myFun_NAwarning <- function() {
warnOccurred <- FALSE
tryCatch(myFun(1:5), HasNA = function(w) warnOcccurred <<- TRUE)
checkTrue(!warnOccurred)
tryCatch(myFun(-(1:5)), HasNA = function(w) warnOcccurred <<- TRUE)
checkTrue(!warnOccurred)
tryCatch(myFun(c(1:5, NA)), HasNA = function(w) warnOccurred <<- TRUE)
checkTrue(warnOccurred)
}
leading to
> test_myFun_NAwarning()
[1] TRUE
Warning message:
In myFun(-(1:5)) : 'x' contains values less than 0
which shows that tryCatch is catching just the particular warning you're interested in, and doing so in a way that does not rely on matching the text of the string. Maybe you'd have a helper function .warn
to make all warnings for your package. See ?withCallingHandlers
for additional detail; withCallingHandlers
and muffleRestart
are how one might cope with continuing evaluation after a warning occurred, rather than stopping the way tryCatch
does.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With